sql:根据列中的值生成列

时间:2016-09-30 04:33:07

标签: sql sql-server sql-server-2008

我有一个包含

等数据的表格
 pr    comm    comm1   comm2     comm    comm1    comm2
         1       1     1         2        2        2 
 123     10      0      1        0        0        0
 234     20      5      10       0        0        0
 345     40      16     21       0        0        0
 098     0        0     0        23       65       76
 765     0        0     0        45       32       0 
 981     65      87     9        45       32       100

我希望结果为

inv

我的意思是我必须为每个{{1}}列值生成新列。

2 个答案:

答案 0 :(得分:1)

如果你不介意去动态。这种方法需要辅助函数将数据转换为EAV结构(实体属性值)。

Declare @YourTable table (PR int,Inv int,Comm int,Comm1 int,Comm2 int)
Insert Into @YourTable values
(123,1,10, 0,  1),
(234,1,20, 5, 10),
(345,1,40,16, 21),
(098,2,23,65, 76),
(765,2,45,32,  0),
(981,1,65,87,  9),
(981,2,45,32,100)

Select PR=Entity
      ,Inv
      ,Col = Concat(B.Attribute,'_',Inv)
      ,B.Value
Into  #Temp
From  @YourTable A
Cross Apply (Select * from [dbo].[udf-EAV]((Select A.* for XML RAW)) Where Attribute<>'Inv' ) B

Declare @SQL varchar(max) = ''
Select @SQL = @SQL+','+QUOTENAME(Col)+'=max(IIF(Col='''+col+''',Value,0))' From (Select Top 100 Percent Inv,Col from #Temp Group By Inv,Col Order by Inv) A
Exec('Select PR'+@SQL+' From #Temp Group By PR Order By Pr')

<强>返回

enter image description here

UDF

CREATE FUNCTION [dbo].[udf-EAV](@XML xml)
Returns Table 
As
Return (
    with cteKey(k) as (Select Top 1 xAtt.value('local-name(.)','varchar(100)') From @XML.nodes('/row') As A(xRow) Cross Apply A.xRow.nodes('./@*') As B(xAtt))    

    Select Entity    = xRow.value('@*[1]','varchar(50)')
          ,Attribute = xAtt.value('local-name(.)','varchar(100)')
          ,Value     = xAtt.value('.','varchar(max)') 
    From  @XML.nodes('/row') As A(xRow)
    Cross Apply A.xRow.nodes('./@*') As B(xAtt)
    Where xAtt.value('local-name(.)','varchar(100)') Not In (Select k From cteKey)
)
/*
-- Notes:  First Field in Query will be the Entity

答案 1 :(得分:1)

首先是UNPIVOT数据,然后做一个PIVOT,如果有很多inv更好地使用动态SQL来生成和运行你的查询:

SELECT  pr,
        COALESCE(inv1_comm,0) as inv1_comm,
        COALESCE(inv1_comm1,0) as inv1_comm1,
        COALESCE(inv1_comm2,0) as inv1_comm2,
        COALESCE(inv2_comm,0) as inv2_comm,
        COALESCE(inv2_comm1,0) as inv2_comm1,
        COALESCE(inv2_comm2,0) as inv2_comm2
FROM (
    SELECT  pr,
            'inv'+CAST(inv as nvarchar(max))+'_'+comms as inv_comms,
            [values]
    FROM (
    SELECT *
    FROM YourTable
    ) as t
    UNPIVOT (
        [values] FOR comms IN (comm,comm1,comm2)
    ) as unpv
) as p
PIVOT (
    MAX([values]) FOR inv_comms IN (inv1_comm,inv1_comm1,inv1_comm2,inv2_comm,inv2_comm1,inv2_comm2)
) as pvt

输出:

pr  inv1_comm   inv1_comm1  inv1_comm2  inv2_comm   inv2_comm1  inv2_comm2
98  0           0           0           23          65          76
123 10          0           1           0           0           0
234 20          5           10          0           0           0
345 40          16          21          0           0           0
765 0           0           0           45          32          0
981 65          87          9           45          32          100

说明:

首先是UNPIVOT部分

SELECT  pr,
        'inv'+CAST(inv as nvarchar(max))+'_'+comms as inv_comms,
        [values]
FROM (
SELECT *
FROM YourTable
) as t
UNPIVOT (
    [values] FOR comms IN (comm,comm1,comm2)
) as unpv

生成此:

pr  inv_comms   values
123 inv1_comm   10
123 inv1_comm1  0
123 inv1_comm2  1
234 inv1_comm   20
234 inv1_comm1  5
234 inv1_comm2  10
345 inv1_comm   40
345 inv1_comm1  16
345 inv1_comm2  21
etc...

因此,我们从3(N)列的一列中获取所有comms

然后我们按照inv的结果集进行PIVOT扩展。

需要

COALESCE才能生成NULL个值 - 0

  

按顺序计算参数,并返回最初未计算为NULL的第一个表达式的当前值。