2011-09-07 28 views
9

¿Cuál es la mejor manera de obtener un conjunto de resultados de pares clave-valor que represente el valor de columna en una fila?Aplanamiento de una tabla de 1 fila en una tabla de pares clave-valor

Dada la siguiente tabla A con sólo 1 fila


Column1 Column2 Column3... 
Value1 Value2 Value3 

Quiero consultar e insertar en otra tabla B:


Key Value 
Column1    Value1 
Column2    Value2 
Column3    Value3 

Un conjunto de columnas en el cuadro A no se conoce en avanzar.

NOTA: Yo estaba mirando PARA funciones de XML y el pivote, así como SQL dinámico para hacer algo como esto:


    DECLARE @sql nvarchar(max) 
    SET @sql = (SELECT STUFF((SELECT ',' + column_name 
           FROM INFORMATION_SCHEMA.COLUMNS 
           WHERE table_name='TableA' 
           ORDER BY column_name FOR XML PATH('')), 1, 1, '')) 
    SET @sql = 'SELECT ' + @sql + ' FROM TableA' 
    EXEC(@sql) 
+3

Este es un '' no es un UNPIVOT' PIVOT' pero todavía se necesita un SQL dinámico. –

+0

+1 por el comentario de Martin. Como no sabe que las columnas están sin guiar, necesita SQL dinámico. – MatBailie

+0

@ Aaron - solo un error tipográfico, corregido ahora – kateroh

Respuesta

18

Una versión que no hay dinámica involucrada. Si tiene nombres de columna que no son válidos para usar como nombres de elemento en XML, esto fallará.

select T2.N.value('local-name(.)', 'nvarchar(128)') as [Key], 
     T2.N.value('text()[1]', 'nvarchar(max)') as Value 
from (select * 
     from TableA 
     for xml path(''), type) as T1(X) 
    cross apply T1.X.nodes('/*') as T2(N) 

Una muestra de trabajo:

declare @T table 
(
    Column1 varchar(10), 
    Column2 varchar(10), 
    Column3 varchar(10) 
) 

insert into @T values('V1','V2','V3') 

select T2.N.value('local-name(.)', 'nvarchar(128)') as [Key], 
     T2.N.value('text()[1]', 'nvarchar(max)') as Value 
from (select * 
     from @T 
     for xml path(''), type) as T1(X) 
    cross apply T1.X.nodes('/*') as T2(N) 

Resultado:

Key     Value 
-------------------- ----- 
Column1    V1 
Column2    V2 
Column3    V3 

actualización

Para una consulta con más de una tabla que podría utilizar for xml auto para obtener la tabla nombres en el XML. Tenga en cuenta que si usa un alias para los nombres de tabla en la consulta, obtendrá el alias.

select X2.N.value('local-name(..)', 'nvarchar(128)') as TableName, 
     X2.N.value('local-name(.)', 'nvarchar(128)') as [Key], 
     X2.N.value('text()[1]', 'nvarchar(max)') as Value 
from (
    -- Your query starts here 
    select T1.T1ID, 
      T1.T1Col, 
      T2.T2ID, 
      T2.T2Col 
    from T1 
     inner join T2 
     on T1.T1ID = T2.T1ID 
    -- Your query ends here 
    for xml auto, elements, type  
    ) as X1(X) 
    cross apply X1.X.nodes('//*[text()]') as X2(N) 

SQL Fiddle

+0

Nice ................! –

+0

Tengo que admitir que este es un enfoque intrigante. – 8kb

+0

local-name (.) Hizo el truco para los nombres de columna. ¡genial gracias! – kateroh

3

Tal vez usted está haciendo esto más complicado de lo que tiene que ser. En parte porque no podía envolver mi pequeño cerebro en el número de combinaciones de PIVOT/UNPIVOT/cualquiera y sería necesario un "mar de rojo" SQL dinámico para lograr esto. Como sabe que la tabla tiene exactamente una fila, extraer el valor de cada columna puede ser solo una subconsulta como parte de un conjunto de consultas ed UNION.

DECLARE @sql NVARCHAR(MAX) = N'INSERT dbo.B([Key], Value) ' 

SELECT @sql += CHAR(13) + CHAR(10) 
     + ' SELECT [Key] = ''' + REPLACE(name, '''', '''''') + ''', 
     Value = (SELECT ' + QUOTENAME(name) + ' FROM dbo.A) UNION ALL' 
FROM sys.columns 
WHERE [object_id] = OBJECT_ID('dbo.A'); 

SET @sql = LEFT(@sql, LEN(@sql)-9) + ';'; 

PRINT @sql; 
-- EXEC sp_executesql @sql; 

Resultado (I sólo creó 4 columnas, pero esto funcionaría para cualquier número):

INSERT dbo.B([Key], Value) 
SELECT [Key] = 'Column1', 
     Value = (SELECT [Column1] FROM dbo.A) UNION ALL 
SELECT [Key] = 'Column2', 
     Value = (SELECT [Column2] FROM dbo.A) UNION ALL 
SELECT [Key] = 'Column3', 
     Value = (SELECT [Column3] FROM dbo.A) UNION ALL 
SELECT [Key] = 'Column4', 
     Value = (SELECT [Column4] FROM dbo.A); 

Lo más eficientes en el mundo? Probablemente no. Pero, de nuevo, para una mesa de una fila, y con suerte una tarea única, creo que funcionará bien. Solo tenga en cuenta los nombres de las columnas que contienen apóstrofes, si permite esas cosas en su tienda ...

EDIT lo siento, no podía dejarlo así. Ahora manejará apóstrofes en nombres de columnas y otras opciones de nombres subóptimas.

6

Creo que estás a mitad de camino. Sólo tiene que utilizar UNPIVOT y dynamic SQL como Martin recomendados:

CREATE TABLE TableA (
    Code VARCHAR(10), 
    Name VARCHAR(10), 
    Details VARCHAR(10) 
) 

INSERT TableA VALUES ('Foo', 'Bar', 'Baz') 
GO 

DECLARE @sql nvarchar(max) 
SET @sql = (SELECT STUFF((SELECT ',' + column_name 
          FROM INFORMATION_SCHEMA.COLUMNS 
          WHERE table_name='TableA' 
          ORDER BY ordinal_position FOR XML PATH('')), 1, 1, '')) 

SET @sql = N'SELECT [Key], Val FROM (SELECT ' + @sql + ' FROM TableA) x ' 
+ 'UNPIVOT (Val FOR [Key] IN (' + @sql + ')) AS unpiv' 
EXEC (@sql) 

Resultados:

Key   Val 
------------ ------------ 
Code   Foo 
Name   Bar 
Details  Baz 

Hay una advertencia, por supuesto. Todas las columnas deberán ser del mismo tipo de datos para que funcione el código anterior. Si no lo están, obtendrá este error:

Msg 8167, Level 16, State 1, Line 1 
The type of column "Col" conflicts with the type of 
other columns specified in the UNPIVOT list. 

Para evitar esto, deberá crear dos enunciados de cadenas de columnas. Uno para obtener las columnas y otro para convertirlos todos como el tipo de datos para su columna Val.

Para varios tipos de columna:

CREATE TABLE TableA (
    Code INT, 
    Name VARCHAR(10), 
    Details VARCHAR(10) 
) 

INSERT TableA VALUES (1, 'Foo', 'Baf') 
GO 

DECLARE 
    @sql nvarchar(max), 
    @cols nvarchar(max), 
    @conv nvarchar(max) 

SET @cols = (SELECT STUFF((SELECT ',' + column_name 
          FROM INFORMATION_SCHEMA.COLUMNS 
          WHERE table_name='TableA' 
          ORDER BY ordinal_position FOR XML PATH('')), 1, 1, '')) 

SET @conv = (SELECT STUFF((SELECT ', CONVERT(VARCHAR(50), ' 
          + column_name + ') AS ' + column_name 
          FROM INFORMATION_SCHEMA.COLUMNS 
          WHERE table_name='TableA' 
          ORDER BY ordinal_position FOR XML PATH('')), 1, 1, '')) 


SET @sql = N'SELECT [Key], Val FROM (SELECT ' + @conv + ' FROM TableA) x ' 
+ 'UNPIVOT (Val FOR [Key] IN (' + @cols + ')) AS unpiv' 
EXEC (@sql) 
Cuestiones relacionadas