【问题标题】:Simplify Dynamic SQL Pivot Table简化动态 SQL 数据透视表
【发布时间】:2014-12-10 22:37:01
【问题描述】:

我根据以下内容编写了一个动态数据透视表查询。这是一个SQL FIDDLE 供参考。

CREATE TABLE TestTable1 ([idnumber] INT, [DataTypeId] INT)
GO
INSERT INTO TestTable1 
VALUES (1, 108), (1, 108), (1, 108), (2, 108), 
       (2, 108), (3, 108), (1, 109),(1, 109),
       (1, 110),(2, 110),(1, 111),(4, 108),(4, 108),
       (4, 110),(4, 111)
GO

这是我写的动态 SQL

DECLARE  @SQL  NVARCHAR(MAX), 
@Cols NVARCHAR(MAX),
@ColsP NVARCHAR(MAX)


SELECT @Cols = STUFF((select  ',  
ISNULL([' + CAST([DataTypeId] as varchar(10)) + '], 0) AS ''' + CAST([DataTypeId] as varchar(10)) + ''''
FROM 
(
SELECT [DataTypeId] FROM [TestTable1] 
GROUP BY [DataTypeId] 
HAVING [DataTypeId] <> '' 
) AS d
ORDER BY [DataTypeId] FOR XML PATH(''),type).value('.','varchar(max)'),1,2,'')

-- /////////////THIS IS WHAT I WANT REMOVED ////////////////////

SELECT @ColsP = STUFF((select  ',  
[' + CAST([DataTypeId] as varchar(10)) + ']'
FROM 
(
SELECT [DataTypeId] FROM [TestTable1] 
GROUP BY [DataTypeId] 
HAVING [DataTypeId] <> '' 
) AS d
ORDER BY [DataTypeId] FOR XML PATH(''),type).value('.','varchar(max)'),1,2,'')

-- /////////////////////////////////////////////////////////////

SET @SQL = 'SELECT idnumber,' + @Cols + ' 
FROM 
(SELECT  idnumber, COUNT([DataTypeId]) AS Total, [DataTypeId] FROM [TestTable1] 
GROUP BY  idnumber, [DataTypeId] 
HAVING [DataTypeId] <> ''''
) p
PIVOT
(
SUM(Total) FOR [DataTypeId] IN (' + @ColsP + ')
) AS pvt 
ORDER BY pvt.idnumber'


-- print @SQL 
EXECUTE( @SQL)

我得到了我想要的结果:

| IDNUMBER | 108 | 109 | 110 | 111 |
|----------|-----|-----|-----|-----|
|        1 |   3 |   2 |   1 |   1 |
|        2 |   2 |   0 |   1 |   0 |
|        3 |   1 |   0 |   0 |   0 |
|        4 |   2 |   0 |   1 |   1 |

但我相信它可以做得更好。我想删除填充变量 @ColsP - SELECT @ColsP = STUFF((select...") 的位置

应该有一种方法可以让我通过TestTable1 循环创建这个动态代码。如您所见,我循环了两次。一次读取要为 select 语句创建的列,一次读取 PIVOT 表。

下面是动态 SQL 生成的代码:

SELECT idnumber, 
  ISNULL([108], 0) AS '108',  
  ISNULL([109], 0) AS '109',  
  ISNULL([110], 0) AS '110',  
  ISNULL([111], 0) AS '111' 
FROM 
(
   SELECT  idnumber, COUNT([DataTypeId]) AS Total, [DataTypeId] 
   FROM [TestTable2] 
   GROUP BY  idnumber, [DataTypeId] 
   HAVING [DataTypeId] <> ''
) p
PIVOT
(
  SUM(Total) FOR [DataTypeId] IN ([108], [109], [110], [111])
) AS pvt 
ORDER BY pvt.idnumber

【问题讨论】:

  • 不确定你在问什么。我不是在这里定义 SP 或函数。这时候简单的一个sql查询。坦克斯

标签: sql-server tsql pivot dynamic-sql


【解决方案1】:

您可以大大缩短您的代码。首先,您可以只使用count 来聚合 PIVOT 中的数据。不需要内部计数来聚合数据或 HAVING 子句。最后,您只需要创建一次列列表。您可以轻松地将代码改进为:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(DataTypeId) 
                    from TestTable1
                    group by DataTypeId
                    order by DataTypeId
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query 
      = N'SELECT idnumber, ' + @cols + N' 
          from 
          (
            select idnumber, DataTypeId
            from TestTable1
          ) x
          pivot 
          (
            count(DataTypeId)
            for DataTypeId in (' + @cols + N')
          ) p '

exec sp_executesql @query;

SQL Fiddle with Demo。这给出了相同的结果:

| IDNUMBER | 108 | 109 | 110 | 111 |
|----------|-----|-----|-----|-----|
|        1 |   3 |   2 |   1 |   1 |
|        2 |   2 |   0 |   1 |   0 |
|        3 |   1 |   0 |   0 |   0 |
|        4 |   2 |   0 |   1 |   1 |

【讨论】:

  • 非常感谢。这正是我一直在寻找的。更好...对这段代码的唯一补充是:在第一个 SELECT 语句中,我添加了:“ HAVING DataTypeId ''” 我的实时数据集确实有带有空字符串的字段,从而创建了一个额外的“空”列(当然不能填……),谢谢
  • @user3204669 您可以改用WHERE DataTypeID &lt;&gt; ''HAVING 将用于聚合过滤。
【解决方案2】:

尝试用这个替换它。

SET NOCOUNT ON
IF OBJECT_ID('TestTable1') IS NOT NULL
DROP TABLE TestTable1
GO

CREATE TABLE TestTable1 ([idnumber] INT, [DataTypeId] INT)
GO

INSERT INTO TestTable1 VALUES 
(1, 108),(1, 108),(1, 108),(2, 108),(2, 108),
(3, 108),(1, 109),(1, 109),(1, 110),(2, 110),
(1, 111),(4, 108),(4, 108),(4, 110),(4, 111)

DECLARE
    @AllColumns NVARCHAR(MAX)

SELECT @AllColumns = ''

SELECT @AllColumns = @AllColumns +
  '[' + CAST(DataTypeId as NVARCHAR)+'],' 
FROM TestTable1
GROUP BY DataTypeId


SET @AllColumns = LEFT(@AllColumns,LEN(@AllColumns)-1)
PRINT @AllColumns

【讨论】:

  • 也许我在你的帖子中遗漏了一些东西。看到我如何为新查询动态创建列,表('TestTable1')的列并没有真正使用。它们是“idnumber”和“DataTypeId”。我需要的是一种非查询、非循环的方式来获取填充到“@ColsP”中的内容(即:“[108]、[109]、[110]、[111]”)。谢谢。
  • 我很抱歉,即使技术相同,这是正确的做法(我编辑了我的答案)。
  • 您好,谢谢。这是非常好的代码。这确实返回了表的列名。我会看看我是否可以合并这个。谢谢。
猜你喜欢
  • 2010-12-28
  • 1970-01-01
  • 2013-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多