Domanda

Ho un set di dati organizzato nel modo seguente:

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

Dove A0001 è il valore A dell'articolo n. 1 e B0001 è il valore B dell'articolo n. 1. In una tabella possono essere presenti più di 60 articoli diversi e ogni articolo ha una colonna di valore A e una colonna di valore B, che significa un totale di oltre 120 colonne nella tabella.

Dove voglio arrivare è un risultato di 3 colonne (indice articolo, valore A, valore B) che somma i valori A e B per ogni articolo:

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

Mentre vado dalle colonne alle righe mi aspetterei un perno nella soluzione, ma non sono sicuro di come risolverlo. Parte del problema è come eliminare A e B per formare i valori per la colonna Indice. L'altra parte è che non ho mai dovuto usare un Pivot prima, quindi mi sto imbattendo anche nella sintassi di base.

Penso che alla fine ho bisogno di avere una soluzione multi-passo che costruisca prima le somme come:

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

Quindi modificare i dati ColName per eliminare l'indice:

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

Infine self join per spostare i valori B in alto accanto ai valori A.

Questo sembra essere un processo lungo e tortuoso per ottenere ciò che voglio. Quindi sto chiedendo consiglio se sto seguendo la strada giusta, o c'è un altro approccio che ho guardato che renderà la mia vita molto più semplice.

Nota 1) La soluzione deve essere in T-SQL su MSSQL 2005.

Nota 2) Il formato della tabella non può essere modificato.

Modifica Un altro metodo a cui ho pensato utilizza UNION e singoli SUM () su ogni colonna:

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

Ma questo approccio non sembra neanche molto carino

MODIFICA Finora ci sono 2 ottime risposte. Ma vorrei aggiungere altre due condizioni alla query :-)

1) Devo selezionare le righe in base a un intervallo di timestamp (minv < timestamp < maxv).

2) Devo anche selezionare in modo condizionale le righe su un UDF che elabora il timestamp

Usando i nomi delle tabelle di Brettski, si tradurrebbe in:

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

Dato che ho aggiunto condizionalmente il requisito fn (), penso che dovrei anche seguire il percorso SQL dinamico come proposto da Jonathon. Soprattutto perché devo creare la stessa query per 12 tabelle diverse, tutte dello stesso stile.

È stato utile?

Soluzione

Stessa risposta qui, è stato divertente:

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

Non è necessario codificare i nomi delle colonne in questo file.

Altri suggerimenti

OK, ho trovato una soluzione che dovrebbe iniziare. Probabilmente ci vorrà del tempo per mettere insieme, ma funzionerà bene. Sarebbe bello se non dovessimo elencare tutte le colonne per nome.

Fondamentalmente si sta utilizzando UNPIVOT e posizionando quel prodotto in una tabella temporanea, quindi eseguendo una query nel set di dati finale. Ho chiamato il mio tavolo ptest quando ho messo insieme, questo è quello con tutte le colonne A0001, ecc.

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

A proposito, grazie per la domanda, questa è stata la prima volta che ho usato UNPIVOT. Ho sempre voluto, non ne ho mai avuto bisogno.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top