SQL 服务器:将行显示为列/数据透视表
SQL Server : show rows as columns / Pivot
我有一个 SQL Server 2008 Express table 这样的:
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)
.
注意:我希望 origamt
和 disamt
的数据类型是 varchar(50)
而不是 Decimal(18, 0)
但它是不是强迫。
我尝试通过参考在 Whosebug 和其他网站上发布的几篇文章来使用 PIVOT,但无法完全完成工作。
这是我尝试过的方法,使用固定的 uid 得到了几乎不错的结果,并且只获取了 origamt
:
select *
from
(
select gid, uid, dat, origamt
from vamounts
) as src
pivot
(
sum(origamt)
for uid IN ( a, b )
) as piv;
请帮助我解决这个问题的最简单的解决方案。我更喜欢最少的代码行和最少的复杂性。
加入可能会有帮助
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,请告诉我,我会为您制作一个示例。
呃,没有。您无法使用 SQL 生成所需的 table。这不是一个有效的枢轴 table.
"the values of uid are not fixed, they may vary dynamically, so,
a_orig, a_dis, b_orig, b_dis, etc cannot be hardcoded into SQL."
抱歉,这也不可能。您必须 指定要作为列 header 放置的确切值。无论何时编写 SELECT
语句,都必须指定要 returning 的列(字段)的名称。没有办法解决这个问题。
但是,以下是根据您的数据创建 "valid" SQL 服务器枢轴 table 所需的步骤:
我不得不承认,当我最近不得不在 SQL 服务器中编写我的第一个 PIVOT
时,我也疯狂地谷歌搜索,但不知道如何写它。
不过,我最终弄清楚了您需要做什么,所以这里是您在其他任何地方都找不到的 step-by-step 指南..!
(读者可以轻松改编这些说明,以使用您自己的数据!)
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 查询,其中 return 正好是三列
第一列将包含将出现在 PIVOT table 的 left-hand 列中的值。
第二列将包含将显示在顶行的值列表。
第三列中的值将根据 row/column headers.
在您的 PIVOT table 中定位
好的,SQL 执行此操作:
SELECT [gid], [uid], [origamt]
FROM tblSomething
这是使用PIVOT
的关键。您的数据库结构可以像您喜欢的那样复杂得可怕,但是当使用 PIVOT
时,您只能使用恰好 三个 个值。不多也不少。
所以,这就是 SQL 将要 return 的结果。我们的目标是创建一个 PIVOT table 包含(仅)这些值:
3。查找 header 行的不同值列表
请注意,在我打算创建的枢轴 table 中,我有三个列(字段),分别称为 a
、b
和 c
。这些是 [uid]
列中的三个唯一值。
因此,要获得这些唯一值的 comma-concatenated 列表,我可以使用此 SQL:
DECLARE @LongString nvarchar(4000)
SELECT @LongString = COALESCE(@LongString + ', ', '') + '[' + [uid] + ']'
FROM [tblSomething]
GROUP BY [uid]
SELECT @LongString AS 'Subquery'
当我 运行 根据您的数据进行此操作时,我得到的是:
现在,剪切并粘贴这个值:我们需要将它 两次 放在我们的整体 SQL SELECT 命令中以创建枢轴 table.
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
...这是一张令人困惑的图片,它显示了组件的来源,以及 运行 执行此命令的结果。
如您所见,关键在于第 2 步中的 SELECT
语句,并将您选择的三个字段放在该命令中的正确位置。
而且,正如我之前所说,数据透视表 table 中的列(字段)来自步骤 3 中获得的值:
[a], [b], [c]
当然,您可以使用这些值的一个子集。也许您只想查看 [a], [b]
的 PIVOT 值并忽略 [c]
.
呸!
所以,这就是如何从您的数据中创建一个枢轴 table。
I will prefer least lines of code and least complexity.
是的,祝你好运..!!!
5.合并两个枢轴 tables
如果您真的想要,可以合并两个这样的 PIVOT table 的内容以获得您正在寻找的确切结果。
这很容易 SQL Shobhit 自己写。
你需要 dynamic SQL 才能获得这些内容。
首先使用您的数据创建 table:
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
我有一个 SQL Server 2008 Express table 这样的:
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)
.
注意:我希望 origamt
和 disamt
的数据类型是 varchar(50)
而不是 Decimal(18, 0)
但它是不是强迫。
我尝试通过参考在 Whosebug 和其他网站上发布的几篇文章来使用 PIVOT,但无法完全完成工作。
这是我尝试过的方法,使用固定的 uid 得到了几乎不错的结果,并且只获取了 origamt
:
select *
from
(
select gid, uid, dat, origamt
from vamounts
) as src
pivot
(
sum(origamt)
for uid IN ( a, b )
) as piv;
请帮助我解决这个问题的最简单的解决方案。我更喜欢最少的代码行和最少的复杂性。
加入可能会有帮助
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,请告诉我,我会为您制作一个示例。
呃,没有。您无法使用 SQL 生成所需的 table。这不是一个有效的枢轴 table.
"the values of uid are not fixed, they may vary dynamically, so, a_orig, a_dis, b_orig, b_dis, etc cannot be hardcoded into SQL."
抱歉,这也不可能。您必须 指定要作为列 header 放置的确切值。无论何时编写 SELECT
语句,都必须指定要 returning 的列(字段)的名称。没有办法解决这个问题。
但是,以下是根据您的数据创建 "valid" SQL 服务器枢轴 table 所需的步骤:
我不得不承认,当我最近不得不在 SQL 服务器中编写我的第一个 PIVOT
时,我也疯狂地谷歌搜索,但不知道如何写它。
不过,我最终弄清楚了您需要做什么,所以这里是您在其他任何地方都找不到的 step-by-step 指南..!
(读者可以轻松改编这些说明,以使用您自己的数据!)
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 查询,其中 return 正好是三列
第一列将包含将出现在 PIVOT table 的 left-hand 列中的值。
第二列将包含将显示在顶行的值列表。
第三列中的值将根据 row/column headers.
在您的 PIVOT table 中定位好的,SQL 执行此操作:
SELECT [gid], [uid], [origamt]
FROM tblSomething
这是使用PIVOT
的关键。您的数据库结构可以像您喜欢的那样复杂得可怕,但是当使用 PIVOT
时,您只能使用恰好 三个 个值。不多也不少。
所以,这就是 SQL 将要 return 的结果。我们的目标是创建一个 PIVOT table 包含(仅)这些值:
3。查找 header 行的不同值列表
请注意,在我打算创建的枢轴 table 中,我有三个列(字段),分别称为 a
、b
和 c
。这些是 [uid]
列中的三个唯一值。
因此,要获得这些唯一值的 comma-concatenated 列表,我可以使用此 SQL:
DECLARE @LongString nvarchar(4000)
SELECT @LongString = COALESCE(@LongString + ', ', '') + '[' + [uid] + ']'
FROM [tblSomething]
GROUP BY [uid]
SELECT @LongString AS 'Subquery'
当我 运行 根据您的数据进行此操作时,我得到的是:
现在,剪切并粘贴这个值:我们需要将它 两次 放在我们的整体 SQL SELECT 命令中以创建枢轴 table.
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
...这是一张令人困惑的图片,它显示了组件的来源,以及 运行 执行此命令的结果。
如您所见,关键在于第 2 步中的 SELECT
语句,并将您选择的三个字段放在该命令中的正确位置。
而且,正如我之前所说,数据透视表 table 中的列(字段)来自步骤 3 中获得的值:
[a], [b], [c]
当然,您可以使用这些值的一个子集。也许您只想查看 [a], [b]
的 PIVOT 值并忽略 [c]
.
呸!
所以,这就是如何从您的数据中创建一个枢轴 table。
I will prefer least lines of code and least complexity.
是的,祝你好运..!!!
5.合并两个枢轴 tables
如果您真的想要,可以合并两个这样的 PIVOT table 的内容以获得您正在寻找的确切结果。
这很容易 SQL Shobhit 自己写。
你需要 dynamic SQL 才能获得这些内容。
首先使用您的数据创建 table:
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