[英]How can I use an SQL Pivot for this?
我有一個按以下方式組織的數據集:
Timestamp|A0001|A0002|A0003|A0004|B0001|B0002|B0003|B0004 ...
---------+-----+-----+-----+-----+-----+-----+-----+-----
2008-1-1 | 1 | 2 | 10 | 6 | 20 | 35 | 300 | 8
2008-1-2 | 5 | 2 | 9 | 3 | 50 | 38 | 290 | 2
2008-1-4 | 7 | 7 | 11 | 0 | 30 | 87 | 350 | 0
2008-1-5 | 1 | 9 | 1 | 0 | 25 | 100 | 10 | 0
...
其中A0001是項#1的值A,而B0001是項#1的值B。 一個表中可以有60多個不同的項目,每個項目都有一個A值列和一個B值列,這意味着表中總共有120多個列。
我想得到的是3列結果(項目索引,A值,B值),該結果將每個項目的A和B值相加:
Index | A Value | B Value
------+---------+--------
0001 | 14 | 125
0002 | 20 | 260
0003 | 31 | 950
0004 | 9 | 10
....
在我從列到行的過程中,我希望解決方案有一個樞紐,但是我不確定如何充實它。 問題的一部分是如何去掉A和B來形成Index列的值。 另一部分是,我以前從未使用過Pivot,因此我也對基本語法有所顧忌。
我認為最終我需要一個多步驟的解決方案,首先將求和構建為:
ColName | Value
--------+------
A0001 | 14
A0002 | 20
A0003 | 31
A0004 | 9
B0001 | 125
B0002 | 260
B0003 | 950
B0004 | 10
然后修改ColName數據以刪除索引:
ColName | Value | Index | Aspect
--------+-------+-------+-------
A0001 | 14 | 0001 | A
A0002 | 20 | 0002 | A
A0003 | 31 | 0003 | A
A0004 | 9 | 0004 | A
B0001 | 125 | 0001 | B
B0002 | 260 | 0002 | B
B0003 | 950 | 0003 | B
B0004 | 10 | 0004 | B
最后,通過自我聯接將B值向上移動到A值旁邊。
獲得我想要的東西似乎是一個漫長的過程。 因此,對於是否要走上正確的道路,我是否有建議,或者是否有我忽略的另一種方法可以使我的生活變得更加輕松。
注意1)解決方案必須在MSSQL 2005的T-SQL中。
注2)表的格式不能更改。
編輯我考慮過的另一種方法是在每一列上使用UNION和單個SUM():
SELECT '0001' as Index, SUM(A0001) as A, SUM(B0001) as B FROM TABLE
UNION
SELECT '0002' as Index, SUM(A0002) as A, SUM(B0002) as B FROM TABLE
UNION
SELECT '0003' as Index, SUM(A0003) as A, SUM(B0003) as B FROM TABLE
UNION
SELECT '0004' as Index, SUM(A0004) as A, SUM(B0004) as B FROM TABLE
UNION
...
但是這種方法看起來也不是很好
編輯到目前為止,有2個好評。 但我想在查詢中添加另外兩個條件:-)
1)我需要基於時間戳范圍(minv <timestamp <maxv)選擇行。
2)我還需要有條件地選擇處理時間戳的UDF上的行
使用Brettski的表名,以上內容將轉換為:
...
(SELECT A0001, A0002, A0003, B0001, B0002, B0003
FROM ptest
WHERE timestamp>minv AND timestamp<maxv AND fn(timestamp)=fnv) p
unpivot
(val for item in (A0001, A0002, A0003, B0001, B0002, B0003)) as unpvt
...
鑒於我有條件地添加了fn()要求,我認為我還需要遵循Jonathon提出的動態SQL路徑。 特別是因為我必須為12個不同的表構建相同的查詢-都是相同的樣式。
同樣的答案在這里,這很有趣:
-- Get column names from system table
DECLARE @phCols NVARCHAR(2000)
SELECT @phCols = COALESCE(@phCols + ',[' + name + ']', '[' + name + ']')
FROM syscolumns WHERE id = (select id from sysobjects where name = 'Test' and type='U')
-- Get rid of the column we don't want
SELECT @phCols = REPLACE(@phCols, '[Timestamp],', '')
-- Query & sum using the dynamic column names
DECLARE @exec nvarchar(2000)
SELECT @exec =
'
select
SUBSTRING([Value], 2, LEN([Value]) - 1) as [Index],
SUM(CASE WHEN (LEFT([Value], 1) = ''A'') THEN Cols ELSE 0 END) as AValue,
SUM(CASE WHEN (LEFT([Value], 1) = ''B'') THEN Cols ELSE 0 END) as BValue
FROM
(
select *
from (select ' + @phCols + ' from Test) as t
unpivot (Cols FOR [Value] in (' + @phCols + ')) as p
) _temp
GROUP BY SUBSTRING([Value], 2, LEN([Value]) - 1)
'
EXECUTE(@exec)
您無需在此代碼中硬編碼列名。
好的,我想出了一個應該讓您入門的解決方案。 可能需要花費一些時間,但效果會很好。 如果我們不必按名稱列出所有列,那就太好了。
基本上,這是使用UNPIVOT並將該產品放入臨時表中,然后將其查詢到最終數據集中。 放在一起時,我為表ptest命名,這是所有A0001等列的表。
-- Create the temp table
CREATE TABLE #s (item nvarchar(10), val int)
-- Insert UNPIVOT product into the temp table
INSERT INTO #s (item, val)
SELECT item, val
FROM
(SELECT A0001, A0002, A0003, B0001, B0002, B0003
FROM ptest) p
unpivot
(val for item in (A0001, A0002, A0003, B0001, B0002, B0003)) as unpvt
-- Query the temp table to get final data set
SELECT RIGHT(item, 4) as item1,
Sum(CASE WHEN LEFT(item, 1) = 'A' THEN val ELSE 0 END) as A,
Sum(CASE WHEN LEFT(item, 1) = 'B' THEN val ELSE 0 END) as B
from #s
GROUP BY RIGHT(item, 4)
-- Delete temp table
drop table #s
順便說一句,謝謝你的提問,這是我第一次使用UNPIVOT。 一直想要,只是從來沒有需要。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.