简体   繁体   English

如何为此使用SQL Pivot?

[英]How can I use an SQL Pivot for this?

I have a data set that is organized in the following manner: 我有一个按以下方式组织的数据集:

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

Where A0001 is Value A of item #1 and B0001 is Value B of item #1. 其中A0001是项#1的值A,而B0001是项#1的值B。 There can be over 60 different items in a table, and each item has an A value column and a B value column, meaning a total of over 120 columns in the table. 一个表中可以有60多个不同的项目,每个项目都有一个A值列和一个B值列,这意味着表中总共有120多个列。

Where I want to get to is a 3 column result (Item index, A Value, B Value) that sums the A and B values for each item: 我想得到的是3列结果(项目索引,A值,B值),该结果将每个项目的A和B值相加:

Index | A Value | B Value
------+---------+--------
 0001 |   14    |   125
 0002 |   20    |   260
 0003 |   31    |   950
 0004 |    9    |    10
 .... 

As I am going from columns to rows I would expect a pivot in the solution, but I am not sure of how to flesh it out. 在我从列到行的过程中,我希望解决方案有一个枢纽,但是我不确定如何充实它。 Part of the issue is how to strip out the A's and B's to form the values for the Index column. 问题的一部分是如何去掉A和B来形成Index列的值。 The other part is that I have never had to use a Pivot before, so I am stumbling over the basic syntax as well. 另一部分是,我以前从未使用过Pivot,因此我也对基本语法有所顾忌。

I think that ultimately I need to have a multi step solution that first builds the summations as: 我认为最终我需要一个多步骤的解决方案,首先将求和构建为:

ColName | Value
--------+------
A0001   |  14
A0002   |  20
A0003   |  31
A0004   |   9
B0001   | 125
B0002   | 260
B0003   | 950
B0004   |  10

Then modify the ColName data to strip out the index: 然后修改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

Finally self join to move the B values up next to the A Values. 最后,通过自我联接将B值向上移动到A值旁边。

This seems to be a long winded process to get what I want. 获得我想要的东西似乎是一个漫长的过程。 So I am after advice as to whether I am headed down the right path, or is there another approach that I have over looked that will make my life so much easier. 因此,对于是否要走上正确的道路,我是否有建议,或者是否有我忽略的另一种方法可以使我的生活变得更加轻松。

Note 1) The solution has to be in T-SQL on MSSQL 2005. 注意1)解决方案必须在MSSQL 2005的T-SQL中。

Note 2) The format of the table cannot be changed. 注2)表的格式不能更改。

Edit Another method I have thought about uses UNIONs and individual SUM()s on each column: 编辑我考虑过的另一种方法是在每一列上使用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
...

But this approach really doesn't look very nice either 但是这种方法看起来也不是很好

EDIT So far there are 2 great responses. 编辑到目前为止,有2个好评。 But I would like to add two more conditions to the query :-) 但我想在查询中添加另外两个条件:-)

1) I need to select the rows based on a range of timestamps (minv < timestamp < maxv). 1)我需要基于时间戳范围(minv <timestamp <maxv)选择行。

2) I also need to conditionally select rows on a UDF that processes the timestamp 2)我还需要有条件地选择处理时间戳的UDF上的行

Using Brettski's table names, would the above translate to: 使用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
...

Given that I have conditionally add the fn() requirement, I think that I also need to go down the dynamic SQL path as proposed by Jonathon. 鉴于我有条件地添加了fn()要求,我认为我还需要遵循Jonathon提出的动态SQL路径。 Especially as I have to build the same query for 12 different tables - all of the same style. 特别是因为我必须为12个不同的表构建相同的查询-都是相同的样式。

Same kinda answer here, that was fun: 同样的答案在这里,这很有趣:

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

You don't need to hard code column names in this one. 您无需在此代码中硬编码列名。

OK, I have come up with one solution which should get you started. 好的,我想出了一个应该让您入门的解决方案。 It will probably take some time to put together, but will perform well. 可能需要花费一些时间,但效果会很好。 It would be nice if we didn't have to list out all the columns by name. 如果我们不必按名称列出所有列,那就太好了。

Basically this is using UNPIVOT and placing that product into a temp table, then querying it into your final data set. 基本上,这是使用UNPIVOT并将该产品放入临时表中,然后将其查询到最终数据集中。 I named my table ptest when I put this together, this is the one with all of the A0001, etc columns. 放在一起时,我为表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

By the way, thanks for the question, this was the first time I got to use UNPIVOT. 顺便说一句,谢谢你的提问,这是我第一次使用UNPIVOT。 Always wanted to, just never had a need. 一直想要,只是从来没有需要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM