Ayuda con consulta PIVOT


12

Tengo una tabla con la siguiente estructura:

CREATE TABLE [dbo].[AUDIT_SCHEMA_VERSION](
    [SCHEMA_VER_MAJOR] [int] NOT NULL,
    [SCHEMA_VER_MINOR] [int] NOT NULL,
    [SCHEMA_VER_SUB] [int] NOT NULL,
    [SCHEMA_VER_DATE] [datetime] NOT NULL,
    [SCHEMA_VER_REMARK] [varchar](250) NULL
);

algunos datos de muestra (parece un problema con sqlfiddle ... por lo que poner algunos datos de muestra):

INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,6,13,CAST('20130405 04:41:25.000' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,6,13,CAST('20130405 04:41:25.000' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,7,13,CAST('20130405 04:41:25.000' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,10,13,CAST('20130405 04:41:25.000' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,12,13,CAST('20130405 04:41:25.000' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,12,13,CAST('20130405 04:41:25.000' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,16,13,CAST('20130405 04:41:25.000' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,16,13,CAST('20130405 04:41:25.000' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,16,13,CAST('20130405 04:41:25.000' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,16,13,CAST('20140417 18:10:44.100' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(2,5,0,CAST('20140417 18:14:14.157' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(2,6,0,CAST('20140417 18:14:23.327' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(2,7,0,CAST('20140417 18:14:32.270' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(2,8,0,CAST('20141209 09:38:40.700' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(2,9,0,CAST('20141209 09:43:04.237' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(2,10,0,CAST('20141209 09:45:19.893' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(2,13,0,CAST('20150323 14:54:30.847' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,10,13,CAST('20130405 04:41:25.000' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,16,14,CAST('20140417 18:11:07.977' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,16,15,CAST('20140417 18:11:13.130' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(2,2,0,CAST('20140417 18:12:11.200' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(2,3,0,CAST('20140417 18:12:33.330' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(2,4,0,CAST('20140417 18:12:48.803' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,13,13,CAST('20130405 04:41:25.000' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(1,16,13,CAST('20130405 04:41:25.000' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(2,11,0,CAST('20141209 09:45:58.993' as DATETIME),'Stored procedure build')
INSERT INTO [AUDIT_SCHEMA_VERSION]([SCHEMA_VER_MAJOR],[SCHEMA_VER_MINOR],[SCHEMA_VER_SUB],[SCHEMA_VER_DATE],[SCHEMA_VER_REMARK])
VALUES(2,12,0,CAST('20141209 09:46:50.070' as DATETIME),'Stored procedure build');

Aquí está el SQLFiddlecon algunos datos de muestra.

¿Puede alguien con experiencia en T-sql guiarme sobre cómo lograr el resultado final? Sé que PIVOT(con columnas dinámicas) será el enfoque correcto, pero no puedo entenderlo.

Resultados previstos :

ingrese la descripción de la imagen aquí

Hasta ahora, tengo a continuación:

select row_number() over (
        partition by CONVERT(varchar(10), SCHEMA_VER_DATE, 110) order by SCHEMA_VER_DATE 
        ) as rownum
    ,CONVERT(varchar(10), SCHEMA_VER_DATE, 110) as UPG_DATE
    ,CONVERT(varchar(1), SCHEMA_VER_MAJOR) + '.' + CONVERT(varchar(2), SCHEMA_VER_MINOR) + '.' + CONVERT(varchar(2), SCHEMA_VER_SUB) as SCHEMA_VER
from audit_schema_version
where SCHEMA_VER_REMARK like 'Stored procedure build'
order by UPGRADE_DATE 

ingrese la descripción de la imagen aquí

Respuestas:


20

Esto es un poco complicado para obtener el resultado final porque tiene múltiples SCHEMA_VERpara cada fecha. Antes de demostrar cómo hacer esto con SQL dinámico, primero mostraré cómo hacerlo con código estático para obtener la lógica correcta. Para obtener el resultado final, puede utilizar tanto pivote como no pivote.

Pero primero, cambiaría su consulta original para usar lo siguiente:

select 
    row_number() over (
    partition by CONVERT(varchar(10), SCHEMA_VER_DATE, 110) order by SCHEMA_VER_MAJOR, SCHEMA_VER_MINOR, SCHEMA_VER_SUB
    ) as minrownum
, row_number() over (
    partition by CONVERT(varchar(10), SCHEMA_VER_DATE, 110) order by SCHEMA_VER_MAJOR desc, SCHEMA_VER_MINOR desc, SCHEMA_VER_SUB desc
    ) as maxrownum
,CONVERT(varchar(10), SCHEMA_VER_DATE, 110) as UPG_DATE
,CONVERT(varchar(1), SCHEMA_VER_MAJOR) + '.' + CONVERT(varchar(2), SCHEMA_VER_MINOR) + '.' + CONVERT(varchar(2), SCHEMA_VER_SUB) as SCHEMA_VER
from audit_schema_version
where SCHEMA_VER_REMARK like 'Stored procedure build';

Ver SQL Fiddle con Demo . Solía row_number()obtener el primero y el último SCHEMA_VERpara cada fecha. Esto es necesario para que pueda concatenar solo esos valores juntos para el comentario.

Luego usaría una tabla temporal para almacenar las filas que tienen una minrownumy maxrownumde 1. La tabla temporal contendría el upg_datey el comment. Esta columna de comentarios contiene una cadena concatenada del par de SCHEMA_VERpara cada fecha.

create table #srcData
(
    upg_date varchar(10),
    comment varchar(500)
);

El código para llenar la tabla temporal sería:

;with cte as
(
  select 
        row_number() over (
        partition by CONVERT(varchar(10), SCHEMA_VER_DATE, 110) order by SCHEMA_VER_MAJOR, SCHEMA_VER_MINOR, SCHEMA_VER_SUB
        ) as minrownum
    , row_number() over (
        partition by CONVERT(varchar(10), SCHEMA_VER_DATE, 110) order by SCHEMA_VER_MAJOR desc, SCHEMA_VER_MINOR desc, SCHEMA_VER_SUB desc
        ) as maxrownum
    ,CONVERT(varchar(10), SCHEMA_VER_DATE, 110) as UPG_DATE
    ,CONVERT(varchar(1), SCHEMA_VER_MAJOR) + '.' + CONVERT(varchar(2), SCHEMA_VER_MINOR) + '.' + CONVERT(varchar(2), SCHEMA_VER_SUB) as SCHEMA_VER
  from audit_schema_version
  where SCHEMA_VER_REMARK like 'Stored procedure build'
)
insert into #srcData
select distinct
    c1.UPG_DATE,
    comment 
        = STUFF((
                  SELECT ' - ' + c2.SCHEMA_VER 
                  FROM cte c2
                  WHERE (c2.minrownum = 1 or c2.maxrownum = 1)
                    and c1.upg_date = c2.upg_date
                  order by c2.minrownum
                  FOR XML PATH(''), TYPE).value('.[1]', 'nvarchar(max)'), 1, 2, '') 
from cte c1
where c1.minrownum = 1 or c1.maxrownum = 1;

Este primer paso a través de sus datos le permite:

|   upg_date |           comment |
|------------|-------------------|
| 03-23-2015 |            2.13.0 |
| 04-05-2013 |  1.6.13 - 1.16.13 |
| 04-17-2014 |   1.16.13 - 2.7.0 |
| 12-09-2014 |    2.8.0 - 2.12.0 |

Ahora aún necesita obtener un recuento de cada fecha del año y el comentario concatenado completo. Aquí sería donde entra en juego el Univot. Puede usar el siguiente código para crear el comentario completo de cada año y obtener el recuento.

select distinct 
    Yr =  right(s1.upg_date, 4),
    cnt = count(*) over(partition by right(s1.upg_date, 4)),
    fullcomment 
            = STUFF((
                      SELECT '; ' + s2.comment 
                      FROM #srcData s2
                      WHERE right(s1.upg_date, 4) = right(s2.upg_date, 4)
                      FOR XML PATH(''), TYPE).value('.[1]', 'nvarchar(max)'), 1, 2, '') 
from #srcData s1;

Ver SQL Fiddle con Demo . Los datos ahora se ven así:

|   Yr | cnt |                       fullcomment |
|------|-----|-----------------------------------|
| 2013 |   1 |                  1.6.13 - 1.16.13 |
| 2014 |   2 |  1.16.13 - 2.7.0;  2.8.0 - 2.12.0 |
| 2015 |   1 |                            2.13.0 |

Como puede ver, tiene varias columnas que deben pivotarse, por lo que puede desvincular tanto la columna fullcommentcomo la cntcolumna en varias filas. Esto se puede hacer usando la función UNPIVOT o CROSS APPLY. Preferiría la aplicación cruzada aquí porque querrás concatenar valores juntos para crear los nuevos nombres de columna:

;with cte as
(
    select distinct 
        Yr =  right(s1.upg_date, 4),
        cnt = count(*) over(partition by right(s1.upg_date, 4)),
        fullcomment 
                = STUFF((
                          SELECT '; ' + s2.comment 
                          FROM #srcData s2
                          WHERE right(s1.upg_date, 4) = right(s2.upg_date, 4)
                          FOR XML PATH(''), TYPE).value('.[1]', 'nvarchar(max)'), 1, 2, '') 
    from #srcData s1
) 
select [2015], [2015_comment], [2014], [2014_comment], [2013], [2013_comment]
from
(
    select c.col, val
    from cte d
    cross apply
    (
        values 
            (Yr, cast(cnt as nvarchar(50))),
            (Yr+'_comment', fullcomment)
    ) c (col, val)  
) d
pivot
(
    max(val)
    for col in ([2015], [2015_comment], [2014], [2014_comment], [2013], [2013_comment])
) piv;

Ver SQL Fiddle con Demo .

Una vez que tenga la lógica, puede convertir esto fácilmente a SQL dinámico.

-- get list of the columns
DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX)

select @cols = STUFF((SELECT  ',' + QUOTENAME(col) 
                    from #srcData
                    cross apply
                    (
                        select right(upg_date, 4), right(upg_date, 4), 2 union all
                        select right(upg_date, 4), right(upg_date, 4)+'_comment', 1
                    ) c (yr, col, so)
                    group by yr, col, so
                    order by yr desc, so desc
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query 
    = 'SELECT ' + @cols + ' 
        from 
        (
            select c.col, val
            from
            (
                select distinct 
                    Yr =  right(s1.upg_date, 4),
                    cnt = count(*) over(partition by right(s1.upg_date, 4)),
                    fullcomment 
                            = STUFF((
                                      SELECT ''; '' + s2.comment 
                                      FROM #srcData s2
                                      WHERE right(s1.upg_date, 4) = right(s2.upg_date, 4)
                                      FOR XML PATH(''''), TYPE).value(''.[1]'', ''nvarchar(max)''), 1, 2, '''') 
                from #srcData s1
            ) d
            cross apply
            (
                values 
                    (Yr, cast(cnt as nvarchar(50))),
                    (Yr+''_comment'', fullcomment)
            ) c (col, val)  
        ) x
        pivot 
        (
           max(val)
           for col in (' + @cols + ')
        ) p '

exec sp_executesql @query;

Ver SQL Fiddle con Demo . Ambas versiones te darán el resultado:

| 2015 | 2015_comment | 2014 |                      2014_comment | 2013 |      2013_comment |
|------|--------------|------|-----------------------------------|------|-------------------|
|    1 |       2.13.0 |    2 |  1.16.13 - 2.7.0;  2.8.0 - 2.12.0 |    1 |  1.6.13 - 1.16.13 |

5

Agregar explicación y un violín: http://sqlfiddle.com/#!6/c92b2/5 .

La consulta a continuación:
1. utiliza una subconsulta para seleccionar las versiones mínimas y máximas por fecha (mín. Y máx. Se aplican a los enteros para garantizar que, por ejemplo, 6 <16)
2. Luego selecciona el año (para agrupar más tarde), Fecha (a orden) y las versiones min - max

SELECT LEFT(UPG_DATE, 4) AS Year
    , UPG_DATE
    , CONVERT(varchar(1), MIN_VER/1000000) + '.' + CONVERT(varchar(2), (MIN_VER/1000 - (MIN_VER/1000000)*1000)) + '.' + CONVERT(varchar(2), MIN_VER%1000)
        + ' - ' + CONVERT(varchar(1), MAX_VER/1000000) + '.' + CONVERT(varchar(2), (MAX_VER/1000 - (MAX_VER/1000000)*1000)) + '.' + CONVERT(varchar(2), MAX_VER%1000) AS Versions
INTO #Versions
FROM (
    SELECT CONVERT(varchar(10), SCHEMA_VER_DATE, 112) as UPG_DATE
        , MIN(SCHEMA_VER_MAJOR*1000000 + SCHEMA_VER_MINOR*1000 + SCHEMA_VER_SUB) AS MIN_VER
        , MAX(SCHEMA_VER_MAJOR*1000000 + SCHEMA_VER_MINOR*1000 + SCHEMA_VER_SUB) AS MAX_VER
    FROM audit_schema_version
    WHERE SCHEMA_VER_REMARK like 'Stored procedure build'
    GROUP BY CONVERT(varchar(10), SCHEMA_VER_DATE, 112)
) Versions;

A continuación, como se repetirá cada columna (año y año_COMENTARIO), se seleccionan dos columnas para identificar los datos. Se cuenta el número de fechas para conocer el número de actualizaciones y las versiones se agrupan por año, por lo que se junta todo en una sola línea. Esto nos da la tabla final que se utilizará para pivotar.

SELECT Year, Year + '_COMMENT' as Year_COMMENT
    , COUNT(Year) AS Upgrades
    , STUFF((SELECT ' ; ' + SUB.Versions
                FROM #Versions SUB
                WHERE SUB.Year = V.Year
                ORDER BY UPG_DATE ASC
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(2000)')
            ,1,3,'') Versions
INTO #GroupedResults
FROM #Versions V
GROUP BY Year

SELECT * FROM #GroupedResults

Aquí están los resultados:

| Year | Year_COMMENT | Upgrades | Versions                         |
|------|--------------|----------|----------------------------------|
| 2013 | 2013_COMMENT | 1        | 1.6.13 - 1.16.13                 |
| 2014 | 2014_COMMENT | 2        | 1.16.13 - 2.7.0 ; 2.8.0 - 2.12.0 |
| 2015 | 2015_COMMENT | 1        | 2.13.0 - 2.13.0                  |

A continuación, una variable se llena con las columnas, ordenadas como queremos mostrarlas:

DECLARE @cols VARCHAR(1000),
    @finalQuery VARCHAR(2000)

SELECT @cols = STUFF((SELECT ',' + QUOTENAME(YEAR) + ',' + QUOTENAME(YEAR + '_COMMENT')
                    FROM #GroupedResults
                    GROUP BY YEAR
                    ORDER BY YEAR DESC
                    FOR XML PATH(''), TYPE
                    ).value('.', 'NVARCHAR(2000)')
    ,1,1,'')

Finalmente, la siguiente consulta utiliza la aplicación cruzada, por lo que obtenemos:
1. La columna de columna llena con los valores Year y Year_COMMENT
2. La columna de valor llena con el número de actualizaciones, en las líneas correspondientes a los años, y los valores de las versiones, en líneas correspondientes a Year_COMMENTs
Se utiliza un pivote sobre las dos columnas resultantes para obtener los valores (número de actualizaciones que alternan con las versiones) a lo largo de la columna (Años alternando con Year_COMMENTs)

set @finalQuery = N'SELECT ' + @cols + N' from 
             (
                select col, value
                from #GroupedResults
                cross apply
                (
                    SELECT CAST(Upgrades AS VARCHAR(200)), Year
                    UNION ALL
                    SELECT CAST(Versions AS VARCHAR(200)), Year_COMMENT
                ) c (value, col)
            ) x
            pivot 
            (
                Min(value)
                for col in (' + @cols + N')
            ) p1
            ; '

EXEC (@finalQuery);

DROP TABLE #Versions;
DROP TABLE #GroupedResults;

Esto devuelve los siguientes resultados:

| 2015 | 2015_COMMENT    | 2014 | 2014_COMMENT                     | 2013 | 2013_COMMENT     |
|------|-----------------|------|----------------------------------|------|------------------|
| 1    | 2.13.0 - 2.13.0 | 2    | 1.16.13 - 2.7.0 ; 2.8.0 - 2.12.0 | 1    | 1.6.13 - 1.16.13 |
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.