1

我有一个按以下方式组织的数据集:

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 以形成索引列的值。另一部分是我以前从未使用过 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
...

但是这种方法看起来也不是很好

编辑 到目前为止,有两个很好的回应。但我想在查询中再添加两个条件:-)

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 个不同的表构建相同的查询——所有表的样式都相同。

4

2 回答 2

5

同样的答案在这里,这很有趣:

-- 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)

您不需要在此列中硬编码列名。

于 2008-11-21T16:31:04.593 回答
1

好的,我想出了一个可以帮助您入门的解决方案。组装起来可能需要一些时间,但会表现良好。如果我们不必按名称列出所有列,那就太好了。

基本上这是使用 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。一直想要,只是从来没有需要。

于 2008-11-21T16:12:54.443 回答