Pregunta

Tengo un conjunto de datos que está organizado de la siguiente manera:

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

Donde A0001 es el Valor A del artículo # 1 y B0001 es el Valor B del artículo # 1. Puede haber más de 60 elementos diferentes en una tabla, y cada elemento tiene una columna de valor A y una columna de valor B, lo que significa un total de más de 120 columnas en la tabla.

Donde quiero llegar es un resultado de 3 columnas (índice de elemento, valor A, valor B) que suma los valores A y B para cada elemento:

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

Como voy de columnas a filas, esperaría un pivote en la solución, pero no estoy seguro de cómo desarrollarlo. Parte del problema es cómo eliminar los A y B para formar los valores de la columna Índice. La otra parte es que nunca he tenido que usar un Pivot antes, así que también estoy tropezando con la sintaxis básica.

Creo que, en última instancia, necesito tener una solución de varios pasos que primero construya las sumas como:

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

Luego modifique los datos de ColName para eliminar el índice:

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

Finalmente, autounirse para mover los valores B hacia arriba al lado de los valores A.

Esto parece ser un proceso largo y sin aliento para obtener lo que quiero. Así que estoy siguiendo consejos sobre si estoy yendo por el camino correcto, o si hay otro enfoque que he pasado por alto que hará que mi vida sea mucho más fácil.

Nota 1) La solución debe estar en T-SQL en MSSQL 2005.

Nota 2) El formato de la tabla no se puede cambiar.

Editar Otro método en el que he pensado utiliza UNIONs y SUM () individuales en cada columna:

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

Pero este enfoque tampoco se ve muy bien

EDITAR Hasta ahora hay 2 excelentes respuestas. Pero me gustaría agregar dos condiciones más a la consulta :-)

1) Necesito seleccionar las filas en función de un rango de marcas de tiempo (minv < marca de tiempo < maxv).

2) También necesito seleccionar condicionalmente filas en un UDF que procesa la marca de tiempo

Usando los nombres de tabla de Brettski, ¿se traduciría lo anterior a:

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

Dado que he agregado condicionalmente el requisito fn (), creo que también necesito seguir la ruta dinámica de SQL propuesta por Jonathon. Especialmente porque tengo que construir la misma consulta para 12 tablas diferentes, todas con el mismo estilo.

¿Fue útil?

Solución

La misma respuesta aquí, fue divertido:

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

No necesita codificar los nombres de las columnas en esta.

Otros consejos

OK, se me ocurrió una solución que debería ayudarlo a comenzar. Probablemente tomará algún tiempo armarlo, pero funcionará bien. Sería bueno si no tuviéramos que enumerar todas las columnas por nombre.

Básicamente, esto es usar UNPIVOT y colocar ese producto en una tabla temporal, luego consultarlo en su conjunto de datos final. Puse el nombre de mi tabla ptest cuando armé esto, este es el que tiene todas las columnas A0001, etc.

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

Por cierto, gracias por la pregunta, esta fue la primera vez que pude usar UNPIVOT. Siempre quise, pero nunca tuve una necesidad.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top