我正在开展一个项目,我有以下要求
我的源表是
PeriodID PeriodName ProductID ProductName Productvalue
-------- ---------- --------- ----------- ------------
10 Jan 100 A 15
20 Feb 100 A 25
30 Mar 100 A 35
10 Jan 200 B 12
20 Feb 200 B 14
30 Mar 200 B 18
10 Jan 300 C 22
20 Feb 300 C 23
30 Mar 300 C 38
我希望表格的输出像这样
目的地表
PeriodID PeriodName A_ID A B_ID B C_ID C
-------- ---------- ---- -- ---- -- ---- --
10 Jan 100 15 200 12 300 22
20 Feb 100 25 200 14 300 23
30 Mar 100 35 200 18 300 38
我正在尝试使用sql server 2008的pivot和unpivot,但我并不熟悉这些运算符的功能。我知道这个要求看起来很奇怪,但这就是我想要的输出结果集。
请帮助我,过去几天我真的遇到麻烦来克服这个问题。
答案 0 :(得分:2)
获得结果的最简单方法是使用聚合函数和CASE表达式:
select
periodid,
periodname,
max(case when productname = 'A' then productid end) A_ID,
max(case when productname = 'A' then Productvalue end) A,
max(case when productname = 'B' then productid end) B_ID,
max(case when productname = 'B' then Productvalue end) B,
max(case when productname = 'C' then productid end) C_ID,
max(case when productname = 'C' then Productvalue end) C
from yourtable
group by periodid, periodname
order by periodid;
但是,如果您想使用PIVOT函数来获得结果,我首先要忽略productid
和productvalue
列,这样您就不再有多列但只有多行此数据。有几种方法可以对数据进行取消,可以使用unpivot功能,也可以将CROSS APPLY与UNION ALL或VALUES一起使用(如果使用的是SQL Server 2008+)。由于您使用的是SQL Server 2008 R2,因此以下是使用CROSS APPLY和VALUES对多列进行取消操作的方法:
select periodid, periodname,
col = productname+col,
value
from yourtable
cross apply
(
values
('_ID', ProductID),
('', Productvalue)
) c (col, value)
见SQL Fiddle with Demo。这会将您的数据转换为以下格式:
| PERIODID | PERIODNAME | COL | VALUE |
|----------|------------|------|-------|
| 10 | Jan | A_ID | 100 |
| 10 | Jan | A | 15 |
| 20 | Feb | A_ID | 100 |
| 20 | Feb | A | 25 |
| 30 | Mar | A_ID | 100 |
现在您可以轻松地将PIVOT功能应用于此数据:
select periodid, periodname,
A_ID, A, B_ID, B, C_ID, C
from
(
select periodid, periodname,
col = productname+col,
value
from yourtable
cross apply
(
values
('_ID', ProductID),
('', Productvalue)
) c (col, value)
) d
pivot
(
max(value)
for col in (A_ID, A, B_ID, B, C_ID, C)
) piv
order by periodid;
最后,如果您拥有未知数量的产品名称,则可以使用动态SQL:
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT ',' + QUOTENAME(productname+col)
from yourtable
cross apply
(
select '_ID', 1 union all
select '', 2
) c (col, so)
group by col, so, productname
order by productname, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT periodid, periodname,' + @cols + '
from
(
select periodid, periodname,
col = productname+col,
value
from yourtable
cross apply
(
values
(''_ID'', ProductID),
('''', Productvalue)
) c (col, value)
) x
pivot
(
max(value)
for col in (' + @cols + ')
) p
order by periodid'
execute sp_executesql @query;
见SQL Fiddle with Demo。所有版本都给出了结果:
| PERIODID | PERIODNAME | A_ID | A | B_ID | B | C_ID | C |
|----------|------------|------|----|------|----|------|----|
| 10 | Jan | 100 | 15 | 200 | 12 | 300 | 22 |
| 20 | Feb | 100 | 25 | 200 | 14 | 300 | 23 |
| 30 | Mar | 100 | 35 | 200 | 18 | 300 | 38 |
答案 1 :(得分:0)
如果组合PeriodID& ProductID是唯一键,以下查询将为您提供所需的结果。
你想要吗?
SELECT PeriodID
, MIN(PeriodName) AS PeriodName
, MIN(A_ID) AS A_ID
, MIN(A) AS A
, MIN(B_ID) AS B_ID
, MIN(B) AS B
, MIN(C_ID) AS C_ID
, MIN(C) AS C
FROM (
SELECT PeriodID
, PeriodName
, CASE ProductID WHEN 'A' THEN ProductID
ELSE NULL
END AS A_ID
, CASE ProductID WHEN 'A' THEN Productvalue
ELSE NULL
END AS A
, CASE ProductID WHEN 'B' THEN ProductID
ELSE NULL
END AS B_ID
, CASE ProductID WHEN 'B' THEN Productvalue
ELSE NULL
END AS B
, CASE ProductID WHEN 'C' THEN ProductID
ELSE NULL
END AS C
, CASE ProductID WHEN 'C' THEN Productvalue
ELSE NULL
END AS C
FROM YOUR_TABLE
)
GROUP BY PeriodID