【问题标题】:Convert row data to column in SQL Server在 SQL Server 中将行数据转换为列
【发布时间】:2013-06-09 04:34:22
【问题描述】:

今天我的同事要求我将数据从垂直暂存表转换为水平表。我的意思是将行转换为列。我使用 PIVOT 并解决了它。但是如果数据字段重复出现,我会遇到移动数据的麻烦。

这是我正在处理的测试数据:

CREATE TABLE STAGING 
(
    ENTITYID INT,
    PROPERTYNAME VARCHAR(25),
    PROPERTYVALUE VARCHAR(25)
)

INSERT INTO STAGING VALUES (1, 'NAME', 'DONNA')
INSERT INTO STAGING VALUES (1, 'SPOUSE', 'HENRY')
INSERT INTO STAGING VALUES (1, 'CHILD', 'JACK')
INSERT INTO STAGING VALUES (2, 'CHILD', 'KAYALA')

我使用 PIVOT 将行数据显示为列:

SELECT * FROM 
(SELECT ENTITYID, PROPERTYNAME, PROPERTYVALUE FROM STAGING) AS T
PIVOT (MAX(PROPERTYVALUE) FOR PROPERTYNAME IN (NAME, SPOUSE, CHILD)) AS T2

输出是:

ENTITYID    NAME    SPOUSE  CHILD
1           DONNA   HENRY   JACK
2           NULL    NULL    KAYALA

但他希望输出类似于:

ENTITYID    NAME    SPOUSE  CHILD   CHILD
1           DONNA   HENRY   JACK    KAYALA

底线是可以有多个 CHILD 属性进入临时表。我们需要考虑这一点,并将所有 CHILDREN 移到列中。

这可能吗?

【问题讨论】:

  • 你怎么知道卡亚拉属于唐娜和亨利?还是因为 Kayala 的实体 ID 也为 1,所以示例错误?
  • 其实有一个支持表,有entityid和parententityid关系。但是,我想为了简化这一点,我可以拥有相同的实体 ID。

标签: sql sql-server sql-server-2008 pivot unpivot


【解决方案1】:

您可以在属性名称中添加一个行号,这样您就可以做您想做的事情:

SELECT * FROM
(
SELECT ENTITYID
       , PROPERTYNAME = PROPERTYNAME + CAST(ROW_NUMBER() OVER(PARTITION BY ENTITYID, PROPERTYNAME ORDER BY PROPERTYVALUE) AS VARCHAR(5))
      ,PROPERTYVALUE
FROM #STAGING   
) AS T
PIVOT (MAX(PROPERTYVALUE) FOR PROPERTYNAME IN (NAME1, SPOUSE1, CHILD1, CHILD2, CHILD3, CHILD4, CHILD5)) AS T2

我在这里假设 ENTITYID 将孩子与父母联系起来,即同一个人的所有孩子的 ENTITYID 为 1,但您的示例显示 Kayala 为 2。

这是一个演示:SQL Fiddle

如果你只想要 CHILD 字段的数字,你可以这样写:

PROPERTYNAME = CASE WHEN PROPERTYNAME LIKE '%CHILD%' THEN PROPERTYNAME + CAST(ROW_NUMBER() OVER(PARTITION BY ENTITYID, PROPERTYNAME ORDER BY PROPERTYVALUE) AS VARCHAR(5))                                                   ELSE PROPERTYNAME END

然后从您的 IN() 语句的其他字段中删除该数字。

额外问题 - 动态执行上述操作: 我们不想假设人们只有一个配偶或 2.3 个孩子,所以我们动态地做这一切:

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

SELECT @cols = STUFF((SELECT ',' + PROPERTYNAME
                    FROM (SELECT DISTINCT PROPERTYNAME = PROPERTYNAME + CAST(ROW_NUMBER() OVER(PARTITION BY ENTITYID, PROPERTYNAME ORDER BY PROPERTYVALUE) AS VARCHAR(5))
                          FROM STAGING )sub
                    ORDER BY CASE WHEN PROPERTYNAME LIKE '%NAME%' THEN 1
                        WHEN PROPERTYNAME LIKE '%SPOUSE%' THEN 2
                        WHEN PROPERTYNAME LIKE '%CHILD%' THEN 3
                    ELSE 4
                    END
                    ,RIGHT(PROPERTYNAME,1) 
                  FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


SET @query = 'SELECT * FROM
                (
                SELECT ENTITYID, PROPERTYNAME = PROPERTYNAME + CAST(ROW_NUMBER() OVER(PARTITION BY ENTITYID, PROPERTYNAME ORDER BY PROPERTYVALUE) AS VARCHAR(5)),PROPERTYVALUE
                FROM STAGING   
                ) AS T
                PIVOT (MAX(PROPERTYVALUE) FOR PROPERTYNAME IN ('+@cols+')) AS T2

'
EXEC(@query)

注意:排序只适用于配偶 1-9 和孩子 1-9,您可以调整以适应,但无论如何都是任意的。

【讨论】:

  • 如果我们知道有多少个孩子,这很有效。但是如果我们不知道限制会发生什么:)
  • 这感觉像是一个额外的问题......但我会屈服。
  • 更新为包含动态版本。
  • :-) 谢谢,这行得通。我应该清楚我的问题。
【解决方案2】:

如果您不需要显示实体 ID,请正确查看您的问题,您是否需要输出:-

ENTITYID    NAME    SPOUSE  CHILD   CHILD
1           DONNA   HENRY   JACK    KAYALA(This one is entityId=2)

所以是不正确的,即使你想要这样的结果:

  NAME    SPOUSE  CHILD   CHILD
  DONNA   HENRY   JACK    KAYALA

SELECT * FROM  (SELECT  PROPERTYNAME, PROPERTYVALUE FROM STAGING) AS T PIVOT (MAX(PROPERTYVALUE) FOR PROPERTYNAME IN (NAME, SPOUSE, CHILD)) AS T2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-10-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多