MS SQL Server 中的动态数据透视

Posted

技术标签:

【中文标题】MS SQL Server 中的动态数据透视【英文标题】:Dynamic Pivot in MS SQL Server 【发布时间】:2016-08-30 15:20:56 【问题描述】:

我正在尝试对我从一个表中获取的最后两列进行动态透视,并加入到另一个表的内容中。我需要将名称值转到标题字段,并在下面相应地填写值值。这是我当前的查询:

USE Innovate
DECLARE @DynamicPivotQuery AS NVARCHAR(MAX)
DECLARE @ColumnName AS NVARCHAR(MAX)

--Get distinct values of the PIVOT Column 
SELECT @ColumnName= ISNULL(@ColumnName + ',','') 
       + QUOTENAME(NAME)

FROM (SELECT DISTINCT NAME FROM Innovate.dbo.Table1 WHERE Name IS NOT NULL) AS ATTRIBUTE_NAME
WHERE Name LIKE 'Suture_-_2nd_Needle_Code'
OR Name LIKE 'Suture_-_Absorbable'
OR Name LIKE 'Suture_-_Antibacterial'
OR Name LIKE 'Suture_-_Armed'
OR Name LIKE 'Suture_-_Barbed'
OR Name LIKE 'Suture_-_Brand_Name'
OR Name LIKE 'Suture_-_C/R_2nd_Needle_Code'
OR Name LIKE 'Suture_-_C/R_Brand_Name'
OR Name LIKE 'Suture_-_C/R_length'
OR Name LIKE 'Suture_-_C/R_Needle_Code'
OR Name LIKE 'Suture_-_Coating'
OR Name LIKE 'Suture_-_Dyed'
OR Name LIKE 'Suture_-_Filament'
OR Name LIKE 'Suture_-_length_inches'
OR Name LIKE 'Suture_-_Looped'
OR Name LIKE 'Suture_-_Material'
OR Name LIKE 'Suture_-_Needle_Code'
OR Name LIKE 'Suture_-_Needle_Shape'
OR Name LIKE 'Suture_-_Needle_Style'
OR Name LIKE 'Suture_-_Noun'
OR Name LIKE 'Suture_-_pleget'
OR Name LIKE 'Suture_-_Popoff'
OR Name LIKE 'Suture_-_Suture_count'
OR Name LIKE 'Suture_-_Suture_size'

--Prepare the PIVOT query using the dynamic 

SET @DynamicPivotQuery = 
  N'SELECT Table1.Primary_Key, Company_Name, Part_Number, Product_Desc, Innovate_Description, ' + @ColumnName + '
    FROM Table1 AS P
    LEFT JOIN Table2 AS A ON P.Primary_Key = A.Primary_Key
    PIVOT(MAX(A.VALUE) 
          FOR A.NAME IN (' + @ColumnName + ')) AS PVTTable'

--Execute the Dynamic Pivot Query
EXECUTE sp_executesql @DynamicPivotQuery

;

这是我不断收到的查询结果:Msg 8156, 级别 16,状态 1,第 5 行指定了“Primary_Key”列 'PVTTable' 多次。消息 4104,第 16 级,状态 1,第 1 行 无法绑定多部分标识符“Table1.Primary_Key”。

谁能帮我在没有错误消息的情况下旋转这些列?我只在代码中指定了一次 Primary_Key,所以我不知道我是如何指定它多次以及它是如何解除绑定的。

【问题讨论】:

首先将 Table1.PrimaryKey 更改为 P.PrimaryKey。接下来,打印出@ColumnName 中的内容并展示给我们。 【参考方案1】:

试试下面的脚本..

SET @DynamicPivotQuery = 
  N'SELECT P.Primary_Key, Company_Name, Part_Number, Product_Desc, Innovate_Description, ' + @ColumnName + '
    FROM Table1 AS P
    LEFT JOIN Table2 AS A ON P.Primary_Key = A.Primary_Key
    PIVOT(MAX(A.VALUE) 
          FOR A.NAME IN (' + @ColumnName + ')) AS PVTTable'

--Execute the Dynamic Pivot Query
EXECUTE sp_executesql @DynamicPivotQuery

【讨论】:

【参考方案2】:

您的 PIVOT 语句存在一些问题。首先,您尝试引用 Table1 和 Table2 的表别名,但这些别名在 PIVOT 的最终选择中不可用,Pivot 命令有点像外部选择,唯一可用的表别名是数据透视别名。

Next pivot 的文档状态“您可以使用 PIVOT 和 UNPIVOT 关系运算符将表值表达式更改为另一个表” (https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx)。基本上,这意味着您需要一个表表达式,其中仅包含您想要参与的唯一命名列,将数据透视传递给 PIVOT 命令。后面的部分可能是问题,因为 Table1 和 Table2 可能都有一个列 Primary_Key 所以 pivot 不理解参考。

要解决此问题,您可以将 Table1 和 2. 连接到内部选择并为该表设置别名,或者构建一个 cte 并在您的命令中使用 cte。这是前一种方式:

SET @DynamicPivotQuery = 
  N'SELECT * FROM
(
SELECT Table1.Primary_Key, Company_Name, Part_Number, Product_Desc, Innovate_Description, ' + @ColumnName + '
    FROM Table1 AS P
    LEFT JOIN Table2 AS A ON P.Primary_Key = A.Primary_Key
) t
    PIVOT(MAX(A.VALUE) 
          FOR NAME IN (' + @ColumnName + ')) AS PVTTable'

【讨论】:

Matt,您建议的查询似乎有效,但我遇到了一个问题,因为无法识别从 Attribute_Name 列旋转的所有新列标题(因为它们不存在)在表 1 中,因此我不断收到这些值的错误,因为它们不在该表中。有没有办法让我从联接中提取这些值以使其能够运行查询? 你能用实际的错误更新你的帖子吗?如果值丢失/注释在您的内部查询中表示,它应该只在您的数据透视中将该列设为 NULL。【参考方案3】:

这些名称值被硬编码以计算 @ColumnName 变量。

如果这些值无论如何都是硬编码的,那么您最好使用连接来运行数据透视,而无需构建要执行的 SQL 语句。

SELECT A.Company_Name, A.Part_Number, A.Product_Desc, A.Innovate_Description, P.*
FROM (select Primary_Key, Name, Value from Innovate.dbo.Table1) T1
PIVOT(MAX(VALUE) FOR NAME IN (
  [Suture_-_2nd_Needle_Code],
  [Suture_-_Absorbable],
  [Suture_-_Antibacterial],
  [Suture_-_Armed],
  [Suture_-_Barbed],
  [Suture_-_Brand_Name],
  [Suture_-_C/R_2nd_Needle_Code],
  [Suture_-_C/R_Brand_Name],
  [Suture_-_C/R_length],
  [Suture_-_C/R_Needle_Code],
  [Suture_-_Coating],
  [Suture_-_Dyed],
  [Suture_-_Filament],
  [Suture_-_length_inches],
  [Suture_-_Looped],
  [Suture_-_Material],
  [Suture_-_Needle_Code],
  [Suture_-_Needle_Shape],
  [Suture_-_Needle_Style],
  [Suture_-_Noun],
  [Suture_-_pleget],
  [Suture_-_Popoff],
  [Suture_-_Suture_count],
  [Suture_-_Suture_size]
  )
) P
LEFT JOIN Innovate.dbo.Table2 AS A ON (A.Primary_Key = P.Primary_Key);

公平地说,这有一个缺点,如果 [Primary_Key] 需要作为第一列,那么 P.* 应该被那些文字列值替换。或者毕竟使用 EXEC 方法。

无论如何,要为该 @ColumnName 变量构建名称列表,可以在没有所有 OR 的情况下完成:

DECLARE @ColumnName NVARCHAR(MAX);
--Get distinct values of the PIVOT Column 
SELECT @ColumnName = ISNULL(@ColumnName + ',','') + QUOTENAME(NAME)
FROM Innovate.dbo.Table1 
WHERE Name Like 'Suture_-_%'
AND SUBSTRING(Name,10,30) IN (
'2nd_Needle_Code',
'Absorbable',
'Antibacterial',
'Armed',
'Barbed',
'Brand_Name',
'C/R_2nd_Needle_Code',
'C/R_Brand_Name',
'C/R_length',
'C/R_Needle_Code',
'Coating',
'Dyed',
'Filament',
'length_inches',
'Looped',
'Material',
'Needle_Code',
'Needle_Shape',
'Needle_Style',
'Noun',
'pleget',
'Popoff',
'Suture_count',
'Suture_size')
GROUP BY Name;

【讨论】:

第一个代码效果很好,因为我可以交换硬编码的值,并且无论如何都会自己指定它们,而不是让 sql 去获取动态枢轴中的值。谢谢!

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

SQL Server 中的动态数据透视表

使用 Sql Developer Oracle 的动态数据透视查询

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

SQL Server 中具有动态列的数据透视表

SQL Server 中的动态透视列

没有聚合函数的 SQL Server 数据透视查询