SQL Rows to Columns,PIVOT可能会工作,但不确定如何使用多列

时间:2011-07-19 16:11:14

标签: tsql sql-server-2008 pivot

我有一个设计糟糕的表,它有行和列的混合,它可能应该有1行,有很多列或完全是其他设计。但这个错误是在20年前,在别人的监视下。

现在我正在通过无数的自我加入来完成我想要的观点。这很痛苦。

以下将介绍我现在的工作方式:

declare @client table
(
    clientNumber int,
    name    varchar(10)
)

insert into @client values (1, 'Bob');
insert into @client values (2, 'Alice');

declare @options table
(
    clientNumber int,
    optionKey varchar(4),
    optionValue1 int,
    optionValue2 int,
    optionValue3 int
)
insert into @options values (1, 'optA', 1, 1, 0);
insert into @options values (1, 'optB', 0, 1, 0);
insert into @options values (2, 'optA', 1, 1, 1);
insert into @options values (2, 'optC', 0, 0, 1);

select c.clientNumber, c.name, 
        oA.optionValue1 as [Graduated], 
        oA.optionValue2 as [Employed], 
        oA.optionValue3 as [Married], 
        oB.optionValue1 as [HasPets], 
        oB.optionValue2 as [LikesThai], 
        oB.optionValue3 as [MathWiz], 
        oC.optionValue1 as [DrvLicense], 
        oC.optionValue2 as [Registered], 
        oC.optionValue3 as [Outdoorsy] 
    from @client c
    left outer join @options oA 
        on oA.clientNumber = c.clientNumber and oA.optionKey = 'optA'
    left outer join @options oB 
        on oB.clientNumber = c.clientNumber and oB.optionKey = 'optB'
    left outer join @options oC 
        on oC.clientNumber = c.clientNumber and oC.optionKey = 'optC'

对于这些结果:

result set from above query

结果集正是我想要的。并非每个客户端都有A,B或C记录,因此结果集中的null正常。经过一段时间的搜索,我找不到这样的例子,所以我不确定PIVOT是不是我正在寻找的。建议?

更新: 这似乎产生了相同的结果。我将在更大的案例上测试它,看看它是否比所有自连接更快。 (我怀疑是)。我还是想知道我是不是用支点吠叫了错误的树。

select clientNumber,
    Min(Case o.optionKey when 'optA' then o.optionValue1 end) [Graduated],
    Min(Case o.optionKey when 'optA' then o.optionValue2 end) [Employed],
    Min(Case o.optionKey when 'optA' then o.optionValue3 end) [Married],
    Min(Case o.optionKey when 'optB' then o.optionValue1 end) [HasPets],
    Min(Case o.optionKey when 'optB' then o.optionValue2 end) [LikesThai],
    Min(Case o.optionKey when 'optB' then o.optionValue3 end) [MathWix],
    Min(Case o.optionKey when 'optC' then o.optionValue1 end) [DrvLicense],
    Min(Case o.optionKey when 'optC' then o.optionValue2 end) [Registered],
    Min(Case o.optionKey when 'optC' then o.optionValue3 end) [Outdoorsy]
    from @options o
    group by clientnumber

2 个答案:

答案 0 :(得分:2)

以下是几个选项。两种解决方案都使用一种技术来支撑数据。第一个解决方案会将您的NULL更改为0。

SELECT c.clientNumber, c.name, 
    MAX(CASE WHEN o.optionKey = 'optA' THEN o.optionValue1 ELSE 0 END) AS [Graduated], 
    MAX(CASE WHEN o.optionKey = 'optA' THEN o.optionValue2 ELSE 0 END) AS [Employed], 
    MAX(CASE WHEN o.optionKey = 'optA' THEN o.optionValue3 ELSE 0 END) AS [Married], 
    MAX(CASE WHEN o.optionKey = 'optB' THEN o.optionValue1 ELSE 0 END) AS [HasPets], 
    MAX(CASE WHEN o.optionKey = 'optB' THEN o.optionValue2 ELSE 0 END) AS [LikesThai], 
    MAX(CASE WHEN o.optionKey = 'optB' THEN o.optionValue3 ELSE 0 END) AS [MathWiz], 
    MAX(CASE WHEN o.optionKey = 'optC' THEN o.optionValue1 ELSE 0 END) AS [DrvLicense], 
    MAX(CASE WHEN o.optionKey = 'optC' THEN o.optionValue2 ELSE 0 END) AS [Registered], 
    MAX(CASE WHEN o.optionKey = 'optC' THEN o.optionValue3 ELSE 0 END) AS [Outdoorsy] 
FROM @client c
LEFT OUTER JOIN @options o 
    ON o.clientNumber = c.clientNumber
GROUP BY c.clientNumber, c.name
ORDER BY c.clientNumber, c.name

第二个解决方案保留NULL值。但是,它需要从BIT到TINYINT的显式类型转换,因为MAX函数在BIT数据类型上失败。

SELECT c.clientNumber, c.name, 
    MAX(CASE WHEN o.optionKey = 'optA' THEN CAST (o.optionValue1 AS TINYINT) END) AS [Graduated], 
    MAX(CASE WHEN o.optionKey = 'optA' THEN CAST (o.optionValue2 AS TINYINT) END) AS [Employed], 
    MAX(CASE WHEN o.optionKey = 'optA' THEN CAST (o.optionValue3 AS TINYINT) END) AS [Married], 
    MAX(CASE WHEN o.optionKey = 'optB' THEN CAST (o.optionValue1 AS TINYINT) END) AS [HasPets], 
    MAX(CASE WHEN o.optionKey = 'optB' THEN CAST (o.optionValue2 AS TINYINT) END) AS [LikesThai], 
    MAX(CASE WHEN o.optionKey = 'optB' THEN CAST (o.optionValue3 AS TINYINT) END) AS [MathWiz], 
    MAX(CASE WHEN o.optionKey = 'optC' THEN CAST (o.optionValue1 AS TINYINT) END) AS [DrvLicense], 
    MAX(CASE WHEN o.optionKey = 'optC' THEN CAST (o.optionValue2 AS TINYINT) END) AS [Registered], 
    MAX(CASE WHEN o.optionKey = 'optC' THEN CAST (o.optionValue3 AS TINYINT) END) AS [Outdoorsy] 
FROM @client c
LEFT OUTER JOIN @options o 
    ON o.clientNumber = c.clientNumber
GROUP BY c.clientNumber, c.name
ORDER BY c.clientNumber, c.name

答案 1 :(得分:0)

我不确定它的表现,但我会这样做:

select c.clientNumber, c.name, 
    oA.optionValue1 as [Graduated], 
    oA.optionValue2 as [Employed], 
    oA.optionValue3 as [Married], 
    oB.optionValue1 as [HasPets], 
    oB.optionValue2 as [LikesThai], 
    oB.optionValue3 as [MathWiz], 
    oC.optionValue1 as [DrvLicense], 
    oC.optionValue2 as [Registered], 
    oC.optionValue3 as [Outdoorsy] 
from @client c 
     OUTER APPLY (SELECT * FROM @options WHERE optionkey = 'optA' And clientnumber = c.clientnumber) oA
     OUTER APPLY (SELECT * FROM @options WHERE optionkey = 'optB' And clientnumber = c.clientnumber) oB
     OUTER APPLY (SELECT * FROM @options WHERE optionkey = 'optC' And clientnumber = c.clientnumber) oC

或CTE可能有用

with t as
(select oA.ClientNumber, 
    oA.optionValue1 as [Graduated], 
    oA.optionValue2 as [Employed], 
    oA.optionValue3 as [Married], 
    oB.optionValue1 as [HasPets], 
    oB.optionValue2 as [LikesThai], 
    oB.optionValue3 as [MathWiz], 
    oC.optionValue1 as [DrvLicense], 
    oC.optionValue2 as [Registered], 
    oC.optionValue3 as [Outdoorsy] 
from 
     (SELECT * FROM @options WHERE optionkey = 'optA') oA
     OUTER APPLY (SELECT * FROM @options WHERE optionkey = 'optB' And clientnumber = oA.clientnumber) oB
     OUTER APPLY (SELECT * FROM @options WHERE optionkey = 'optC' And clientnumber = oA.clientnumber) oC )

 select c.clientNumber, c.name,
    t.[Graduated], 
    t.[Employed], 
    t.[Married], 
    t.[HasPets], 
    t.[LikesThai], 
    t.[MathWiz], 
    t.[DrvLicense], 
    t.[Registered], 
    t.[Outdoorsy] 
from @client c join t on c.clientnumber = t.clientnumber