2011-10-19 47 views
9

Estoy trabajando en una consulta SQL usando pvots con columnas dinámicas en SQL Server (T-sql). En lugar de enviar mi larga consulta, estoy ilustrando mi problema con un modelo simplificado.Pivotes con columnas dinámicas en SQL Server

que crear 2 tablas: Tabla 1 y Tabla 2 y rellenarlas con algunas entradas de la siguiente manera:

Tabla 1:


Col_ID1 ............ ... col_name

1 ......................... ene-11

2 ........ ................. Feb-11

3 ......................... Mar-11

Tabla 2:


Col_ID2 ...... Cuenta ..... Nombre de cuenta ...... Cantidad

1 ............... 121 ......... ..Electricidad ............ 10000

2 ............... 121 ........... Electricidad. ........... 20000

3 ............... 121 ........... Electricidad ............ 30000

1 ............... 122 ........... Teléfono .............. 100

2. .............. 122 ........... Teléfono .............. 200

3 ... ............ 122 ........... Teléfono .............. 300

Estoy creando un Pivote, pero quiero que los nombres de las columnas se generen paramétricamente (en base a las fechas introducidas desde la entrada s creen), y no codificado.

La siguiente consulta funciona bien, pero da sólo unas pocas columnas como SS:

Ene-11 Feb-11 ........... ........ ... Mar-11

10,000.00 20,000.00 ...... ...... 30,000.00

100,00 200,00 ............... .... ....... 300.00

Deseo que la consulta también devuelva las columnas descriptivas, como sigue:

Cuenta ........... Nombre de cuenta ........... Ene-11 ............ Feb-11 .. ............ Mar-11

121 ................. Electricidad ............ ...... 10,000.00 ...... 20,000.00 .......... 30,000.00

122 ................. Teléfono .. ................... 100.00 ........... 200.00 ............. 300.00

¿Alguien podría ayudarme a modificar mi consulta para que pueda lograr mi objetivo?

Esta consulta es una adaptación de la siguiente artículo escrito por el Dr. Andras en septiembre de 2007. http://www.simple-talk.com/community/blogs/andras/archive/2007/09/14/37265.aspx

Alguien comentó que el código podría ser objeto de ataques de inyección y propone utilizar la función QUOTENAME en lugar de la concatenación entre corchetes .

¿Podría explicarme cómo usar Quotename en mi consulta?

Muchas gracias,

Leon Lai .
.
.

Aquí es mi consulta:

------------------------ crear & tabla1 poblar ---------- ----------------------

CREATE TABLE Table1 
(Col_ID1 INT, 
Col_Name varchar(10)) 

INSERT INTO Table1 VALUES (1, 'Jan-11') 
INSERT INTO Table1 VALUES (2, 'Feb-11') 
INSERT INTO Table1 VALUES (3, 'Mar-11') 

--------------------- ---- crear & poblar tabla2 ----------------------------------

CREATE TABLE Table2 
(Col_ID2 INT, 
Account varchar(10), 
AccountName varchar(20), 
Amount numeric(18,6)) 

INSERT INTO Table2 VALUES (1, 121, 'Electricity', 10000) 
INSERT INTO Table2 VALUES (2, 121, 'Electricity', 20000) 
INSERT INTO Table2 VALUES (3, 121, 'Electricity', 30000) 
INSERT INTO Table2 VALUES (1, 122, 'Telephone', 100)   
INSERT INTO Table2 VALUES (2, 122, 'Telephone', 200) 
INSERT INTO Table2 VALUES (3, 122, 'Telephone', 300) 

- --------------------------------- crear encabezados de columnas -------------- -----

DECLARE @cols NVARCHAR(2000) 
SELECT @cols = STUFF((SELECT DISTINCT TOP 100 PERCENT 
'],[' + t2.Col_Name 
FROM Table1 AS t2 
ORDER BY '],[' + t2.Col_Name 
FOR XML PATH('') 
), 1, 2, '') + ']' 

------------------------------------- create @query --- -------------------

DECLARE @query NVARCHAR(4000) 

SET @query = N'SELECT '+ 
@cols +' 

FROM 

------------------------ --subquery -----

(SELECT
t1.Col_Name,
t2.Account,
t2.Amount
FROM Table1 AS t1
JOIN Table2 AS t2 ON t1.Col_ID1 = t2.Col_ID2
) p

-------------------- pivote ------------ -------------

PIVOT
(
Sum ([Amount])
FOR Col_Name IN
('+
@cols +')
) AS pvt '

---------------------- exec & gota ----------

EXECUTE(@query) 
drop table table1 
drop table table2 

=== =============================================== ==

Hola Felipe,

Muchas gracias por su respuesta.

Su consulta propuesta funciona sin problemas y genera la pantalla esperada, pero no es exactamente lo que quería.

En primer lugar, gracias por el código: SELECT @cols = isnull (@cols + '' '') + '[' + col_name + ']'

Es más simple y reemplaza mi línea involucrando cosas y ruta xml, aparentemente con el mismo efecto.

Déjame explicarte lo que quiero hacer.

Deseo desarrollar una consulta en Sap Business 1 (Un paquete de contabilidad o llámelo ERP). Sap usa T-sql en Microsoft Server 2008, y tiene su propio generador de consultas. Con muy pocas excepciones, Sap sql es similar a T-sql.

Quiero que mi consulta proporcione una lista de todos los ingresos y gastos mes a mes durante un período de 12 meses.

Sin embargo, yo no quiero que mi encabezados de columna para ser codificados, (ya que esto requeriría me enmendar mi consulta de vez en cuando) de la siguiente manera:

Ene-11, 11-Feb, Mar- 11, abr-11, ..... Dic-11

Más bien, quiero que los encabezados de las columnas se generen dinámicamente a partir de las fechas que el usuario ingrese en la pantalla de entrada.

Como mencioné, la consulta que publiqué en el foro es una versión demasiado simplificada de mi consulta real, que se usa solo a título ilustrativo. La consulta real contiene varias variables y una pantalla de entrada (llamada Query - Cuadro de Criterios de Selección en Sap b1) permite al usuario ingresar una fecha. Es esta fecha la que se usará para determinar dinámicamente los nombres de las columnas.

Esta es la razón por lo que necesitaba este tipo de herramientas complicadas como @cols, @query, etc. pivote

Si de entrada, digo '01 .06.11' (01 jun 2011) en la pantalla de entrada, esta fecha se pasado al sql que determinará los nombres de los títulos de las columnas como sigue:

Jun-11, Jul-11, Aug-11 ..... May-12.

Si otra fecha de entrada, digo '01 .09.10' (01 sep 2010), los títulos de las columnas cambiará a:

Sep-10, 10-Oct, .... 11-Ago

Parece que ha codificado mis encabezados de columna.

¿Podría echar un segundo vistazo a mi consulta y proponer algo que permita que los nombres de las columnas se generen paramétricamente en lugar de codificarse?

Gracias

Leon Lai

Respuesta

11

suma de estas columnas es muy simple. La pregunta final sería

SELECT Account, AccountName, [Feb-11],[Jan-11],[Mar-11] FROM 
(SELECT 
t1.Col_Name, 
t2.Account, 
t2.AccountName, 
t2.Amount 
FROM Table1 AS t1 
JOIN Table2 AS t2 ON t1.Col_ID1 = t2.Col_ID2 
) p 
PIVOT 
(
Sum ([Amount]) 
FOR Col_Name IN 
([Feb-11],[Jan-11],[Mar-11]) 
) AS pvt 

que tiene t2.AccountName añade a la subconsulta, y cuenta y AccountName añadido a la SELECT inicial. Los echan en el comunicado de construcción y ya está:

DECLARE @query NVARCHAR(4000) 
SET @query = N'SELECT Account, AccountName, ' + @cols +' FROM 

(SELECT 
t1.Col_Name, 
t2.Account, 
t2.AccountName, 
t2.Amount 
FROM Table1 AS t1 
JOIN Table2 AS t2 ON t1.Col_ID1 = t2.Col_ID2 
) p 

PIVOT 
(
Sum ([Amount]) 
FOR Col_Name IN 
('+ 
@cols +') 
) AS pvt ' 

En cuanto a la inyección de SQL, la única manera que puedo ver que pasa es que si alguien alguna manera incrusta código malicioso dentro de Table1.Col_Name, y si usted tiene que preocuparse sobre eso, tienes problemas más grandes que "bloquear" esta consulta dinámica.

También vale la pena mencionar que usaría lo siguiente para compilar la lista de columnas (@Cols) porque es más corta y fácil de leer, pero sobre todo porque no me gusta XML.

DECLARE @cols NVARCHAR(2000)  
SELECT @cols = isnull(@cols + ',', '') + '[' + Col_Name + ']' 
FROM Table1 
ORDER BY Col_Name 
+0

Hola, Philip, he posteado una pregunta a tu respuesta Gracias Leon –

+0

¡Esta es una respuesta ridículamente buena! – jTC

+0

Use 'quotename (Col_Name)' en lugar de ''[' + Col_Name + ']''. – jnm2

1

Añadiendo otra respuesta, ya que esta la edita casi una segunda pregunta. (Sin detalles y detalles, solo puedo ofrecer esquemas generales y código de psuedo; no conozco SAP).

Comencemos con el pivote. Necesita generar columnas etiquetadas por, presumiblemente, mes, que tenía en su ejemplo como Table1.Col_Name, varchar (10); esos valores se extraen y se agregan dinámicamente a la consulta dinámica como nombres de columna. Si no existe dicha columna en la base de datos, entonces debe construirla para la consulta en función de los datos ingresados ​​por el usuario. Trabajaré con las siguientes suposiciones: - Los datos tienen una columna de fecha y hora, donde se puede encontrar cualquier valor (año a milisegundo) - El usuario especifica una "fecha de inicio" (¿es siempre el primero de un mes?) y debe generar columnas para eso y los siguientes 11 meses, agregando los datos que caen dentro de cada mes objetivo.

Paso 1, me gustaría configurar y poblar una tabla temporal que contiene las columnas de destino: 12

CREATE TABLE #Months 
(
    Col_Name varchar(10) 
    ,MonthStart datetime 
    ,MonthEnd datetime 
) 

etiqueta tiene el formato que desea que aparezca, MonthStart sería el comienzo absoluto del mes (digamos, 1 de octubre de 2011 00: 00: 00,000), y MonthEnd sería el comienzo absoluto del próximo mes (1 de noviembre de 2011 00: 00: 00,000) - esto le permite usar SELECT … from <table> where DataDate >= MontStart and DataDate < MonthEnd para obtener todos los datos dentro de ese mes.

A continuación, unirse a esta tabla en la tabla de datos y agregados, algo así como:

SELECT 
    mt.Col_Name 
    ,sum(dt.RawData) Amount 
from #Months mt 
    inner join MyData dt 
    on dt.DataDate >= mt.MonthStart 
    and dt.DataDate < mt.MonthEnd -- Yes, ON clauses don't have to be simple equivalencies! 
    inner join <other tables as necessary for Account, AccountName, etc.> 

enchufar esto como la consulta interna de la declaración de pivote, extracto/construir la lista de Col_Names de la tabla temporal utilizando la consulta que no es XML (no sé cómo llamarlo), compila y ejecuta dinámicamente, y deberías estar bien.

+0

Gracias Philip Leon Lai –

Cuestiones relacionadas