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