Как я могу использовать SQL Pivot для этого? - PullRequest
1 голос
/ 21 ноября 2008

У меня есть набор данных, который организован следующим образом:

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 - это значение A элемента № 1, а B0001 - это значение B элемента № 1. В таблице может быть более 60 различных элементов, и каждый элемент имеет столбец значения A и столбец значения B, что означает в общей сложности более 120 столбцов в таблице.

Я хочу получить результат из трех столбцов (индекс элемента, значение 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) Решение должно быть в T-SQL на MSSQL 2005.

Примечание 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

2) Мне также нужно условно выбрать строки в UDF, который обрабатывает метку времени

Используя имена таблиц Бреттского, вышеприведенные слова переведут в:

...
(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 (), я думаю, что мне также нужно идти по динамическому пути SQL, предложенному Джонатоном. Тем более, что мне нужно создать один и тот же запрос для 12 разных таблиц - все в одном стиле.

Ответы [ 2 ]

5 голосов
/ 21 ноября 2008

Такой же любопытный ответ, это было весело:

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

Вам не нужно жестко кодировать имена столбцов в этом.

1 голос
/ 21 ноября 2008

ОК, я придумала одно решение, которое должно помочь вам начать. Вероятно, это займет некоторое время, но все будет хорошо. Было бы неплохо, если бы нам не приходилось перечислять все столбцы по имени.

В основном это использует 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. Всегда хотел, просто никогда не нуждался.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...