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 を超える列があります。

取得したいのは、各アイテムの A 値と B 値を合計する 3 列の結果 (アイテム インデックス、A 値、B 値) です。

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

列から行に移動するときに、ソリューションにピボットが期待されますが、具体化する方法がわかりません。問題の一部は、A と B を取り除いて Index 列の値を形成する方法です。もう 1 つの部分は、これまで 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つの素晴らしい回答があります。しかし、クエリにさらに 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 の異なるテーブル (すべて同じスタイル) に対して同じクエリを作成する必要があるためです。

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

OK、私はあなたが始めるべき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 に答える