SQL Server:将行显示为列/ Pivot

时间:2016-05-31 07:52:51

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

我有一个像这样的SQL Server 2008 Express表:

rno   gid   uid   dat          origamt   disamt
-----------------------------------------------
1     AA    a     12-05-2016    200       210
2     AA    b     12-05-2016    300       305
3     AA    c     12-05-2016    150       116
4     BB    a     12-05-2016    120       125
5     BB    c     12-05-2016    130       136
6     CC    a     12-05-2016    112       115
7     CC    b     12-05-2016    135       136
不同日期的

等等

我想这样表现出来:

sno     dat       gid   a_orig   a_dis   b_orig  b_dis   c_orig   c_dis .....
1    12-05-2016   AA    200      210     300     305     150      116
2    12-05-2016   BB    120      125     0       0       130      136
3    12-05-2016   CC    112      115     135     136     0        0

注意:uid的值不是固定的,它们可能会动态变化,因此,a_orig,a_dis,b_orig,b_dis等不能硬编码到SQL中。

注意:由于gid和uid的笛卡尔积,预计每个日期约有300行。我会通过实现LIKE子句来搜索日期,因为dat列的数据类型是varchar(50)

注意:我希望origamtdisamt的数据类型为varchar(50)而不是Decimal(18, 0),但这不是强制性的。

我尝试使用PIVOT,参考stackoverflow和其他网站上发布的几篇文章,但无法完成工作。

以下是我尝试过的内容,使用固定的uid得到了几乎所有的结果,并且仅获取了origamt

select *
from 
(
  select gid, uid, dat, origamt
  from vamounts
) as src
pivot
(
  sum(origamt)
  for uid IN ( a, b )
) as piv;

请帮助我解决此问题最简单的解决方案。我会更喜欢最少的代码行和最少的复杂性。

3 个答案:

答案 0 :(得分:2)

Errr,不。您无法使用SQL生成所需的表。这不是一个有效的数据透视表。

  

" uid的值不固定,它们可能会动态变化,因此,   a_orig,a_dis,b_orig,b_dis等不能硬编码到SQL中。"

抱歉,这也是不可能的。您必须指定要作为列标题放置的确切值。无论何时编写SELECT语句,都必须指定要返回的列(字段)的名称。没有办法解决这个问题。

但是,以下是创建"有效"所需的步骤。数据中的SQL Server数据透视表:

enter image description here

我必须承认,当我最近不得不在SQL Server中编写我的第一个PIVOT时,我也像疯了一样搜索,但不知道如何编写它。

但是,我最终得出了你需要做的事情,所以这里是你在其他任何地方都找不到的循序渐进的指南......!

(读者可以轻松调整这些说明,与您自己的数据一起使用!)

<强> 1。创建示例数据

如果您希望读者回复您的问题,您至少应该为他们提供SQL来创建您的示例数据,这样他们就可以解决这些问题。

所以,这里是我如何创建问题中显示的数据:

CREATE TABLE tblSomething
(
    [gid] nvarchar(100),
    [uid] nvarchar(100),
    [dat] datetime,
    [origamt] int,
    [disamt] int
)
GO

INSERT INTO tblSomething VALUES ('AA', 'a', '2016-05-12', 200, 210)
INSERT INTO tblSomething VALUES ('AA', 'b', '2016-05-12', 300, 305)
INSERT INTO tblSomething VALUES ('AA', 'c', '2016-05-12', 150, 116)
INSERT INTO tblSomething VALUES ('BB', 'a', '2016-05-12', 120, 125)
INSERT INTO tblSomething VALUES ('BB', 'c', '2016-05-12', 130, 136)
INSERT INTO tblSomething VALUES ('CC', 'a', '2016-05-12', 112, 115)
INSERT INTO tblSomething VALUES ('CC', 'b', '2016-05-12', 135, 136)
GO

<强> 2。编写一个SQL查询,它只返回三列

第一列将包含将出现在PIVOT表的左栏中的值。

第二列将包含将出现在顶行的值列表。

第三列中的值将根据行/列标题定位在PIVOT表中。

好的,这是执行此操作的SQL:

SELECT [gid], [uid], [origamt]
FROM tblSomething

这是使用PIVOT的关键。您的数据库结构可能非常复杂,但使用PIVOT时,您只能使用三个值。不多也不少。

所以,这就是SQL将返回的内容。我们的目标是创建一个包含(仅)这些值的PIVOT表:

enter image description here

第3。查找标题行的不同值列表

请注意,在我想要创建的数据透视表中,我有三个名为abc的列(字段)。这是您[uid]列中的三个唯一值。

因此,要获得这些唯一值的逗号连接列表,我可以使用此SQL:

DECLARE @LongString nvarchar(4000)

SELECT @LongString = COALESCE(@LongString + ', ', '') + '[' + [uid] + ']' 
FROM [tblSomething]
GROUP BY [uid]

SELECT @LongString AS 'Subquery'

当我针对您的数据运行此操作时,我得到的是:

enter image description here

现在,剪切并粘贴此值:我们需要在整个SQL SELECT命令中两次来创建数据透视表。

<强> 4。全部放在一起

这是一个棘手的问题。

您需要将步骤2中的SQL命令和步骤3的结果合并为一个SELECT命令。

这是你的SQL的样子:

SELECT [gid],
        --  Here's the "Subquery" from part 3
        [a], [b], [c]
FROM (
    --  Here's the original SQL "SELECT" statement from part 2
    SELECT [gid], [uid], [origamt]
    FROM tblSomething

) tmp ([gid], [uid], [origamt])
pivot (
    MAX([origamt]) for [uid] in (
        --  Here's the "Subquery" from part 3 again
        [a], [b], [c]
    )
) p

...这里有一个令人困惑的图像,它显示了组件的来源以及运行此命令的结果。

enter image description here

正如您所看到的,关键是第2步中的SELECT语句,并将您选择的三个字段放在此命令的正确位置。

而且,正如我之前所说,数据透视表中的列(字段)来自第3步中获得的值:

[a], [b], [c]

当然,您可以使用这些值的子集。也许您只想查看[a], [b]的PIVOT值并忽略[c]

P!

那么,这就是如何从数据中创建数据透视表。

  

我更喜欢最少的代码行和最少的复杂性。

是的,祝你好运.. !!!

<强> 5。合并两个数据透视表

如果你真的想要,你可以合并两个这样的PIVOT表的内容,以获得你正在寻找的确切结果。

对于Shobhit来说这很容易写自己的SQL。

enter image description here

答案 1 :(得分:0)

加入可能有帮助

declare @t table (rno int,  gid varchar(2),  uid varchar(1),  dat varchar(10), origamt int,  disamt int)
insert into @t
values 
(1,     'AA',    'a',     '12-05-2016',    200,       210),
(2 ,    'AA',    'b',     '12-05-2016',    300,       305),
(3  ,   'AA',    'c',     '12-05-2016',    150,       116),
(4 ,    'BB',    'a',     '12-05-2016',    120,       125),
(5 ,    'BB',    'c',     '12-05-2016',    130,       136),
(6 ,    'CC',    'a',     '12-05-2016',    112,       115),
(7 ,    'CC',    'b',     '12-05-2016',    135,       136)

    select   -- piv.*,piv2.*
             piv.gid,piv.dat
            ,piv.a as a_org
            ,piv2.a as a_dis
            ,piv.b as b_org
            ,piv2.b as b_dis
            ,piv.c as c_org
            ,piv2.c as c_dis
    from 
    (
      select gid, uid, dat, origamt
      from  @t
    ) as src
    pivot
    (
      sum(origamt)
      for uid IN ([a],[b],[c] )
    ) as piv
    join 
    (select piv2.*
    from 
    (
      select gid, uid, dat, disamt
      from  @t
    ) as src
    pivot
    (
      sum(disamt)
      for uid IN ([a],[b],[c] )
    ) as piv2
    ) piv2
    on piv2.gid = piv.gid and piv2.dat = piv.dat 

这是一个POC,你必须使用动态sql来处理可变数量的uid。如果您不知道如何使用动态SQL,请告诉我,我会为您编写一个示例。

答案 2 :(得分:0)

这个东西需要动态 SQL。

首先使用您的数据创建表格:

CREATE TABLE #temp (
    rno int, 
    gid nvarchar(10), 
    [uid] nvarchar(10), 
    dat date, 
    origamt int, 
    disamt int 
) 

INSERT INTO #temp VALUES 
(1,     'AA',    'a',     '12-05-2016',    200,       210),
(2,     'AA',    'b',     '12-05-2016',    300,       305),
(3,     'AA',    'c',     '12-05-2016',    150,       116),
(4,     'BB',    'a',     '12-05-2016',    120,       125),
(5,     'BB',    'c',     '12-05-2016',    130,       136),
(6,     'CC',    'a',     '12-05-2016',    112,       115),
(7,     'CC',    'b',     '12-05-2016',    135,       136)

然后使用列声明变量:

DECLARE @columns nvarchar(max), @sql nvarchar(max), @columns1 nvarchar(max), @columnsN nvarchar(max)
--Here simple columns like [a],[b],[c] etc
SELECT @columns =STUFF((SELECT DISTINCT ','+QUOTENAME([uid]) FROM #temp FOR XML PATH('')),1,1,'')
--Here with ISNULL operation ISNULL([a],0) as [a],ISNULL([b],0) as [b],ISNULL([c],0) as [c]
SELECT @columnsN = STUFF((SELECT DISTINCT ',ISNULL('+QUOTENAME([uid])+',0) as '+QUOTENAME([uid]) FROM #temp FOR XML PATH('')),1,1,'')
--Here columns for final table orig.a as a_orig, dis.a as a_dis,orig.b as b_orig, dis.b as b_dis,orig.c as c_orig, dis.c as c_dis
SELECT @columns1 = STUFF((SELECT DISTINCT ',orig.'+[uid] + ' as ' +[uid]+ '_orig, dis.'+[uid] + ' as ' +[uid]+ '_dis' FROM #temp FOR XML PATH('')),1,1,'')

主要查询:

SELECT @sql = '
SELECT  orig.gid,
        orig.dat,
        '+@columns1+'
FROM (
        SELECT gid, dat, '+@columnsN+'
        FROM (
            SELECT gid, [uid], LEFT(dat,10) as dat, origamt
            FROM #temp
            ) as p
        PIVOT (
        SUM(origamt) FOR [uid] in ('+@columns+')
        ) as pvt
    ) as orig
LEFT JOIN (
        SELECT gid, dat, '+@columnsN+'
        FROM (
            SELECT gid, [uid], LEFT(dat,10) as dat, disamt
            FROM #temp
            ) as p
        PIVOT (
        SUM(disamt) FOR [uid] in ('+@columns+')
        ) as pvt
    ) as dis
    ON dis.gid = orig.gid and dis.dat = orig.dat'

EXEC(@sql)

输出:

gid dat         a_orig  a_dis   b_orig  b_dis   c_orig  c_dis
AA  2016-12-05  200     210     300     305     150     116
BB  2016-12-05  120     125     0       0       130     136
CC  2016-12-05  112     115     135     136     0       0