未知数量列的 SQL Server 动态数据透视

Posted

技术标签:

【中文标题】未知数量列的 SQL Server 动态数据透视【英文标题】:SQL Server Dynamic pivot for an unknow number of columns 【发布时间】:2018-10-11 13:45:04 【问题描述】:

之前有人问过这个问题,但在一个稍微不同的情况下(似乎不适合我的问题)所以..

我的数据看起来像这样

Name  |Item       |Note
George|Paperclip  |Two boxes
George|Stapler    |blue one
George|Stapler    |red one
George|Desk lamp  |No light bulb
Mark  |Paperclip  |One box 2"
Mark  |Paperclip  |One box 4"
Mark  |Block Notes|a blue one
..?   |..?        |..?

我想通过名称来获取

Name  |Paperclip|Stapler|Desk Lamp|Block Notes
George|        1|      2|        1| NULL
Mark  |        2| NULL  | NULL    |          1

我已经关注了类似的例子 Convert Rows to columns using 'Pivot' in SQL Server 但我离解决方案还很远……有人可以帮帮我吗? 谢谢!

编辑:实际代码

drop table #temp2
SELECT DISTINCT *,
CASE WHEN Item IS NULL THEN NULL ELSE COUNT(Item) OVER(PARTITION BY Name) END CNT 
    INTO #TEMP2
    FROM [ISPBIGFIX].[dbo].[C_INV_ErroriTavolette_v11]

DECLARE @cols NVARCHAR (MAX)
DECLARE @Columns2 NVARCHAR (MAX)

SET @cols = SUBSTRING((SELECT DISTINCT ',['+Item+']' FROM #TEMP2 GROUP BY Item FOR XML PATH('')),2,8000)

SET @Columns2 = SUBSTRING((SELECT DISTINCT ',ISNULL(['+Item+'],0) AS ['+Item+']' FROM #TEMP2 GROUP BY Item FOR XML PATH('')),2,8000)


DECLARE @query NVARCHAR(MAX)
SET @query = 'SELECT Name,' + @Columns2 + ' FROM 
             (
                 SELECT Name,ErrorType,CNT FROM #TEMP2
             ) x
             PIVOT  
             (
                 SUM(CNT)
                 FOR [Item] IN (' + @cols + ')
            ) p
            WHERE Name IS NOT NULL;'



EXEC SP_EXECUTESQL @query

【问题讨论】:

您能告诉我们您当前的查询吗? 添加了代码,但结果当然不是我想要的 您可以不使用从表中选择的查询,而是为#TEMP2 发布 ddl,然后发布一些插入语句,以便我们可以重现这一点? “ddl”是什么意思? 似乎其他人已经把它放在一起了。但是ddl = data definition language。换句话说,创建表语句。 【参考方案1】:

试试这个,它遵循这里提到的相同示例:Convert Rows to columns using 'Pivot' in SQL Server

--Drop Sample temp Table     

    IF OBJECT_ID('tempdb..#temp2') IS NOT NULL
    BEGIN
        DROP TABLE #temp2
    END

--create Sample temp Table 

    create Table #temp2
    (
    [name] varchar(255),
    Item varchar(255),
    note varchar(255)
    )

--Insert Sample Data

    insert into #temp2
    values( 'George','Paperclip','Two boxes'),
    ('George','Stapler','blue one'),
    ('George','Stapler','red one'),
    ('George','Desk lamp','No light bulb'),
    ('Mark','Paperclip','One box 2'),
    ('Mark','Paperclip','One box 4'),
    ('Mark','Block Notes','a blue one')

DECLARE @cols AS NVARCHAR(MAX), @cols2 AS NVARCHAR(MAX),
@query  AS NVARCHAR(MAX)

--Generate Columns from Data
--Generate Columns from Data

select @cols = STUFF((SELECT ', isnull(' + QUOTENAME(Item)  + ',0) as' +  QUOTENAME(Item)
                    from #temp2
                    group by Item
                    order by Item
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

select @cols2 = STUFF((SELECT ', ' + QUOTENAME(Item)  
                    from #temp2
                    group by Item
                    order by Item
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


--Pivot Query
    set @query = 'SELECT [name],' + @cols + ' from 
                 (
                      select [Name], Item, count(*) as xcount
                   from #temp2
                   group by  Name, Item
                ) x
                pivot 
                (
                    sum(xCount)
                    for Item in (' + @cols2+ ')
                ) p '

    execute(@query);

--Drop Sample Temp Table

    IF OBJECT_ID('tempdb..#temp2') IS NOT NULL
    BEGIN
        DROP TABLE #temp2
    END

【讨论】:

非常快,不依赖于临时表.. 真的很棒,谢谢!但是如果需要 0(零)而不是 Null 怎么办? 效果很好,谢谢!只是为了更好地理解它.. isnull(' + QUOTENAME(ErrorType) + ',0) 是这样做的吗? (0而不是空值?) isnull() 是用指定的替换值替换 NULL 的函数。以下是 microsoft 提供的文档:docs.microsoft.com/en-us/sql/t-sql/functions/…【参考方案2】:

试试这个动态 Sql

IF OBJECT_ID('dbo.TT')IS NOT NULL
DROP TABLE TT
;WITH CTE(Name  ,Item ,Note)
AS
(
SELECT 'George','Paperclip'  ,'Two boxes'     UNION ALL
SELECT 'George','Stapler'    ,'blue one'      UNION ALL
SELECT 'George','Stapler'    ,'red one'       UNION ALL
SELECT 'George','Desk lamp'  ,'No light bulb' UNION ALL
SELECT 'Mark'  ,'Paperclip'  ,'One box 2'     UNION ALL
SELECT 'Mark'  ,'Paperclip'  ,'One box 4'     UNION ALL
SELECT 'Mark'  ,'Block Notes','a blue one'
)
SELECT *,CASE WHEN Item IS NOT NULL THEN 1 ELSE 0 END AS Item2 INTO TT FROM CTE

SELECT * FROM TT

DECLARE @Sql nvarchar(max),
        @Sqlcol  nvarchar(max),
        @ISNULLSqlcol nvarchar(max)

SELECT  @Sqlcol=STUFF((SELECT  DISTINCT  ', '+QUOTENAME(Item) 
                FROM TT  FOR XML PATH ('')),1,1,'')

SELECT  @ISNULLSqlcol=STUFF((SELECT DISTINCT  ', '+'ISNULL(SUM('+QUOTENAME(Item) +'),''0'') AS '+QUOTENAME(Item)
                FROM TT  FOR XML PATH ('')),1,1,'')

SET @Sql='SELECT Name,'+@ISNULLSqlcol+'FROM 
         (
          SELECT * FROM TT
          ) AS SRc
          PIVOT
          (
          SUM(Item2) FOR Item IN('+@Sqlcol+')
          ) AS Pvt GROUP BY Name'

Print @Sql
EXEC (@Sql)

【讨论】:

【参考方案3】:

试试这个查询(我认为它更清楚)。我使用 Kashif Qureshi 的代码创建一个临时表,但我的代码在 PIVOT 部分不同

--Drop Sample temp Table     

    IF OBJECT_ID('tempdb..#temp2') IS NOT NULL
    BEGIN
        DROP TABLE #temp2
    END

--create Sample temp Table 

    create Table #temp2
    (
    [name] varchar(255),
    Item varchar(255),
    note varchar(255)
    )

--Insert Sample Data

    insert into #temp2
    values( 'George','Paperclip','Two boxes'),
    ('George','Stapler','blue one'),
    ('George','Stapler','red one'),
    ('George','Desk lamp','No light bulb'),
    ('Mark','Paperclip','One box 2'),
    ('Mark','Paperclip','One box 4'),
    ('Mark','Block Notes','a blue one')

    --- PIVOT
    DECLARE @v_query  VARCHAR(8000) -- main query
    DECLARE @v_columns VARCHAR(8000) -- columns

    SET @v_columns =''

    -- Get string columns
    SELECT @v_columns += '[' + CONVERT(VARCHAR, Item) +'],' FROM (SELECT DISTINCT Item FROM #temp2) AS temp

    -- Delete the last comma
    SET @v_columns = LEFT(@v_columns,LEN(@v_columns)-1)


    -- Main query

    SET @v_query = 'SELECT Name, ' + @v_columns +' FROM
                    (
                        SELECT Name, Item FROM #temp2
                    ) T             
                    PIVOT
                    (
                        Count(Item)
                        FOR Item IN ('+ @v_columns +')
                    ) PVT'
    EXEC (@v_query)

-- DROP
    IF OBJECT_ID('tempdb..#temp2') IS NOT NULL
    BEGIN
        DROP TABLE #temp2
    END

【讨论】:

以上是关于未知数量列的 SQL Server 动态数据透视的主要内容,如果未能解决你的问题,请参考以下文章

动态 SQL 透视查询中的分组和聚合函数

SQL Server 数据透视查询 - 问题

动态反透视和拆分列 SQL Server 2012

SQL Server:多列的动态数据透视

SQL Server - 动态数据透视表 - SQL 注入

SQL Server 数据库中的动态数据透视