Sqlserver table Row to column data - pivot table

匆匆过客 提交于 2019-12-11 02:37:43

问题


I have an Excel-sheet as below, I already dump the data into database (as the sample data below) from this excel in normalized form.

Now I want to get the similar view of excel from database's data. I tried this, but given in wrong format. Good if somebody given the same result view as excel with column name and inner join.

I do not want to hardcore as the year expand.

declare @tblyear table(id int, year int)
insert into @tblyear values (1,2012), (2,2013),(3,2014) ,(4,2015),(5,2016)

declare @ChargeableYearDetails table ( id int, year int, CumulativeHrs numeric(18,2), CumulativeChargeableHrs  numeric(18,2))

--take only 2 row year wise for the sample
insert into @ChargeableYearDetails values 
(1, 1, 1657.75, 1243.50),
(2, 1, 3925.50, 3044.75),
(3, 2, 870.25, 568.25),
(4, 2, 2517.75, 1808.00),
(5, 3, 189.50, 99.00),
(6, 3, 1982.75, 1295.25),
(7, 4, 539.00, 351.00),
(8, 4, 2542.75, 1924.75),
(9, 5, 874.50, 596.50),
(9, 5, 2721.50, 2175.50)


select * from @tblyear
select * from @ChargeableYearDetails

/*I tried this , but given wrong result*/
select * from @ChargeableYearDetails
pivot 
(
    max(CumulativeHrs)
    FOR year in ([1],[2],[3],[4],[5])
) as p

回答1:


My answer is a little bit complicated, but I should post it. I use dynamic sql and pivoting.

DECLARE @columnsH nvarchar(500),
        @columnsCH nvarchar(500),
        @columns nvarchar(1000),
        @sql nvarchar(4000)

CREATE TABLE #tblyear (id int, [year] int)
INSERT INTO #tblyear VALUES (1,2012), (2,2013),(3,2014) ,(4,2015),(5,2016)

CREATE TABLE #ChargeableYearDetails (id int, [year] int, CumulativeHrs numeric(18,2), CumulativeChargeableHrs  numeric(18,2))
INSERT INTO #ChargeableYearDetails VALUES 
(1, 1, 1657.75, 1243.50),(2, 1, 3925.50, 3044.75),(3, 2, 870.25, 568.25),
(4, 2, 2517.75, 1808.00),(5, 3, 189.50, 99.00),(6, 3, 1982.75, 1295.25),
(7, 4, 539.00, 351.00),(8, 4, 2542.75, 1924.75),(9, 5, 874.50, 596.50),
(9, 5, 2721.50, 2175.50)

SELECT @columnsH = STUFF((SELECT DISTINCT ',' + QUOTENAME('CumulativeHrsYY'+ CAST([Year] AS NVARCHAR(4))) FROM #tblyear FOR XML PATH('')),1,1,'')
SELECT @columnsCH = STUFF((SELECT DISTINCT ',' + QUOTENAME('CumulativeChargeableHrs'+ CAST([Year] AS NVARCHAR(4))) FROM #tblyear FOR XML PATH('')),1,1,'')
SELECT @columns = STUFF((SELECT DISTINCT ',' + QUOTENAME('CumulativeHrsYY'+ CAST([Year] AS NVARCHAR(4))) +',' + QUOTENAME('CumulativeChargeableHrs'+ CAST([Year] AS NVARCHAR(4))) FROM #tblyear FOR XML PATH('')),1,1,'')

SELECT @sql = '

SELECT '+ @columns+'
FROM (
SELECT *
FROM (
    SELECT ''CumulativeHrsYY''+ CAST(t.[Year] AS NVARCHAR(4)) as [Year], 
            c.CumulativeHrs, 
            ROW_NUMBER() OVER (PARTITION BY c.[year] ORDER BY c.[year]) as rn
    FROM #ChargeableYearDetails c
    LEFT JOIN #tblyear t
        ON t.ID = c.[year]
    ) as t
pivot 
(
    max(CumulativeHrs)
    FOR [year] in ('+@columnsH+')
) as p
) as part1
LEFT JOIN (
SELECT *
FROM (
    SELECT ''CumulativeChargeableHrs''+ CAST(t.[Year] AS NVARCHAR(4)) as [Year],
            c.CumulativeChargeableHrs,
            ROW_NUMBER() OVER (PARTITION BY c.[year] ORDER BY c.[year]) as rn
    FROM #ChargeableYearDetails c
    LEFT JOIN #tblyear t
        ON t.ID = c.[year]
    ) as t
pivot 
(
    max(CumulativeChargeableHrs)
    FOR [year] in ('+@columnsCH+')
) as p
) as part2
ON part1.rn = part2.rn'

EXEC(@sql)

DROP TABLE #ChargeableYearDetails
DROP TABLE #tblyear

Output:

CumulativeHrsYY2012 CumulativeChargeableHrs2012 CumulativeHrsYY2013 CumulativeChargeableHrs2013 CumulativeHrsYY2014 CumulativeChargeableHrs2014 CumulativeHrsYY2015 CumulativeChargeableHrs2015 CumulativeHrsYY2016 CumulativeChargeableHrs2016
1657.75             1243.50                     870.25              568.25                      189.50              99.00                       539.00              351.00                      874.50                  596.50
3925.50             3044.75                     2517.75             1808.00                     1982.75             1295.25                     2542.75             1924.75                     2721.50             2175.50



回答2:


Using dynamic sql, something like this will work. Have changed your table var to temp tables. You will have to change it to accommodate for multiple rows. Just add a group by to the pivot. Pls refer this msdn blog post on how to pivot on multiple fields

DECLARE @cols AS NVARCHAR(MAX), @cols2 AS NVARCHAR(MAX), @query  AS NVARCHAR(MAX)
select @cols = STUFF((SELECT ',' + QUOTENAME('Cumulative Hours ' + cast(year as varchar(4))) 
                    from #tblyear
                    group by year, id
                    order by id
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')
select @cols2 = STUFF((SELECT ',' + QUOTENAME('Cumulative Chargable Hours ' + cast(year as varchar(4))) 
                    from #tblyear
                    group by year, id
                    order by id
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

DECLARE @colsMax AS NVARCHAR(MAX), @cols2Max AS NVARCHAR(MAX)
select @colsMax = STUFF((SELECT ', MAX(' + QUOTENAME('Cumulative Hours ' + cast(year as varchar(4))) + ') ' + QUOTENAME('Cumulative Hours ' + cast(year as varchar(4)))
                    from #tblyear
                    group by year, id
                    order by id
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')
select @cols2Max = STUFF((SELECT ', MAX(' + QUOTENAME('Cumulative Chargable Hours ' + cast(year as varchar(4))) + ') ' + QUOTENAME('Cumulative Chargable Hours ' + cast(year as varchar(4)))
                    from #tblyear
                    group by year, id
                    order by id
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = N'SELECT ' + @colsMax + ',' + @cols2Max + N' from 
             (
                select CumulativeHrs, CumulativeChargeableHrs
                , ''Cumulative Hours '' +  cast(b.year as varchar(4)) as [CumHours]
                , ''Cumulative Chargable Hours '' +  cast(b.year as varchar(4)) as [CumChargableHours]
                from #ChargeableYearDetails a join #tblyear b on a.year = b.id
            ) query
            pivot 
            (
                max(CumulativeHrs)
                for CumHours in (' + @cols + N')
            ) p
            pivot 
            (
                max(CumulativeChargeableHrs)
                for CumChargableHours in (' + @cols2 + N')
            ) p2 

            '
print @query
exec sp_executesql @query;



回答3:


You can do this using dynamic sql.. this will create some MAX(CASE WHEN) statements for each year/hrs combo.. then execute the sql by using EXEC sp_executesql @sql or just EXEC(@sql)

DECLARE @Sql NVARCHAR(MAX),
        @Cols NVARCHAR(MAX)

SELECT  @Cols = COALESCE(@Cols + ', ', '') +
        'MAX(CASE WHEN Year = ' + CAST(id AS VARCHAR) + ' THEN CumulativeHrs END) AS [Cumulative Hrs YTD - ' + CAST([year] AS VARCHAR) + '], ' +
        'MAX(CASE WHEN Year = ' + CAST(id AS VARCHAR) + ' THEN CumulativeChargeableHrs END) AS [Cumulative Chargeable Hrs ' + CAST([year] AS VARCHAR) + '] ' 
FROM    tblyear
ORDER BY [year] DESC

SET     @Sql = 'SELECT ' + @Cols 
                + ' FROM (SELECT *, Row_number() Over (Partition by [year] order by id) Rn FROM ChargeableYearDetails) t '
                + ' GROUP BY Rn'


EXEC sp_executesql @Sql

you won't be able to use table variables in this type of query without defining them as types first, but i'm guessing you're using actual tables.

Row_number was also added to break each record per year into separate rows




回答4:


It is possible to use the PIVOT clause, but this has two disadvantages. Firstly, it can only pivot a single column. Secondly it can't handle dynamic columns; you have to pre-specify them.

In this example, I'm using two CTEs to pivot each column, then joining the result sets.

You might do better to implement this in a mid-tier layer programmed in C#.

WITH cteHrs AS
(
    SELECT id, [1], [2], [3], [4], [5]
        FROM
            (SELECT id, [year], CumulativeHrs
                FROM @ChargeableYearDetails) AS H
        PIVOT
        (
            SUM(CumulativeHrs)
            FOR [year] IN ([1], [2], [3], [4], [5])
        ) as pvtH
),
cteChgHrs AS
(
    SELECT id, [1], [2], [3], [4], [5]
        FROM
            (SELECT id, [year], CumulativeChargeableHrs
                FROM @ChargeableYearDetails) AS C
        PIVOT
        (
            SUM(CumulativeChargeableHrs)
            FOR [year] IN ([1], [2], [3], [4], [5])
        ) as pvtC
)
SELECT COALESCE(C.id, H.id) AS 'id',
        C.[1] AS 'Cum Hrs 2012', H.[1] AS 'Cum Chg Hrs 2012',
        C.[2] AS 'Cum Hrs 2013', H.[2] AS 'Cum Chg Hrs 2013',
        C.[3] AS 'Cum Hrs 2014', H.[3] AS 'Cum Chg Hrs 2014',
        C.[4] AS 'Cum Hrs 2015', H.[4] AS 'Cum Chg Hrs 2015',
        C.[5] AS 'Cum Hrs 2016', H.[5] AS 'Cum Chg Hrs 2016'
    FROM cteHrs AS H
        FULL OUTER JOIN cteChgHrs AS C
            ON C.id = H.id;

The result set is this.



来源:https://stackoverflow.com/questions/37051655/sqlserver-table-row-to-column-data-pivot-table

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!