I have searched this website for all possible solutions but still can't find an answer for my Pivot problem.
I have a table with the following data.
Portfolio | Date | TotalLoans | ActiveLoans | TotalBalance
--------------------------------------------------------------------
P1 | 2015-12-31 | 1,000 | 900 | 100,000.00
P1 | 2015-11-30 | 1,100 | 800 | 100,100.00
P1 | 2015-10-31 | 1,200 | 700 | 100,200.00
I am trying to create a pivot with the following output (only where Portfolio = P1)
Field | 2015-12-31 | 2015-11-30 | 2015-10-31 |
-----------------------------------------------------
TotalLoans | 1,000 | 1,100 | 1,200 |
ActiveLoans | 900 | 800 | 700 |
TotalBalance | 100,000 | 100,100 | 100,200 |
Ideally, I am looking for a dynamic pivot, but a static query would do as well and I can try a dynamic query out of that.
You need first to UNPIVOT your table. You can do it using this query:
SELECT Portfolio, [Date], Val, ColType
FROM (SELECT Portfolio,
[Date],
TotalLoans,
ActiveLoans,
TotalBalance
FROM mytable
WHERE Portfolio = 'P1') AS srcUnpivot
UNPIVOT (
Val FOR ColType IN (TotalLoans, ActiveLoans, TotalBalance)) AS unpvt
Output:
Portfolio Date Val ColType
===============================================
P1 2015-12-31 1000 TotalLoans
P1 2015-12-31 900 ActiveLoans
P1 2015-12-31 100000 TotalBalance
P1 2015-11-30 1100 TotalLoans
P1 2015-11-30 800 ActiveLoans
P1 2015-11-30 100100 TotalBalance
P1 2015-10-31 1200 TotalLoans
P1 2015-10-31 700 ActiveLoans
P1 2015-10-31 100200 TotalBalance
Note: All unpivoted fields must be of the same type. The query above assumes a type of int for all fields. If this is not the case then you have to use CAST.
Using the above query you can apply PIVOT:
SELECT Portfolio, ColType, [2015-12-31], [2015-11-30], [2015-10-31]
FROM (
... above query here ...
PIVOT (
MAX(Val) FOR [Date] IN ([2015-12-31], [2015-11-30], [2015-10-31])) AS pvt
This is Giorgos Betsos solution as dynamic SQL. This will deal without the need to write the date values explicitly.
Please: If you like this: Do not mark this solution as accepted, set the acceptance to Giorgos Betsos. There's the hard work! But you may vote on it :-)
CREATE TABLE #tbl(Portfolio VARCHAR(10),[Date] DATE,TotalLoans DECIMAL(10,4),ActiveLoans DECIMAL(10,4),TotalBalance DECIMAL(10,4));
INSERT INTO #tbl VALUES
('P1','20151231',1000,900,100000.00)
,('P1','20151130',1100,800,100100.00)
,('P1','20151031',1200,700,100200.00);
DECLARE #pvtColumns VARCHAR(MAX)=
(
STUFF(
(
SELECT DISTINCT ',['+CONVERT(VARCHAR(10), [Date] ,126) + ']'
FROM #tbl
FOR XML PATH('')
)
,1,1,'')
);
DECLARE #cmd VARCHAR(MAX)=
'SELECT Portfolio, ColType, ' + #pvtColumns +
' FROM (
SELECT Portfolio, [Date], Val, ColType
FROM (SELECT Portfolio,
[Date],
TotalLoans,
CAST(ActiveLoans AS DECIMAL(10,4)) AS ActiveLoans,
TotalBalance
FROM #tbl AS mytable
WHERE Portfolio = ''P1'') AS srcUnpivot
UNPIVOT (
Val FOR ColType IN (TotalLoans, ActiveLoans, TotalBalance)) AS unpvt
) AS srcPivot
PIVOT (
MAX(Val) FOR [Date] IN (' + #pvtColumns + ')) AS pvt';
EXEC (#cmd);
You need to use Dyanmic SQL and construct them. See examples at http://social.technet.microsoft.com/wiki/contents/articles/17510.t-sql-dynamic-pivot-on-multiple-columns.aspx
Here is the code
CREATE procedure CrossTab
(
#select varchar(2000),
#PivotCol varchar(100),
#Summaries varchar(100),
#GroupBy varchar(100),
#OtherCols varchar(100) = Null
)
AS
set nocount on
set ansi_warnings off
declare #sql varchar(8000)
Select #sql = ''
Select #OtherCols= isNull(', ' + #OtherCols,'')
create table #pivot_columns (pivot_column_name varchar(100))
Select #sql='select ''' + replace( + #PivotCol,',',''' as pivot_column_name union all select ''')+''''
insert into #pivot_columns
exec(#sql)
select #sql=''
create table #pivot_columns_data (pivot_column_name varchar(100),pivot_column_data varchar(100))
Select #PivotCol=''
Select #PivotCol=min(pivot_column_name) from #pivot_columns
While #PivotCol>''
Begin
insert into #pivot_columns_data(pivot_column_name,pivot_column_data)
exec
(
'select distinct ''' + #PivotCol +''' as pivot_column_name, convert(varchar(100),' + #PivotCol + ') as pivot_column_data from
('+
#select
+'
) T'
)
Select #PivotCol=min(pivot_column_name) from #pivot_columns where pivot_column_name>#PivotCol
end
select
#sql = #sql + ', ' +
replace(
replace(
#Summaries,'(','(CASE WHEN ' + Pivot_Column_name + '=''' +
pivot_column_data + ''' THEN '
),
')[', ' END) as [' + pivot_column_data
)
from #pivot_columns_data
order by pivot_column_name
exec
(
'select ' + #GroupBy +#OtherCols +#sql +
' from (
'+
#select
+'
) T
GROUP BY ' + #GroupBy
)
drop table #pivot_columns
drop table #pivot_columns_data
set nocount off
set ansi_warnings on
Usage
EXEC CrossTab
'SELECT LastName, OrderDate,shipcountry FROM northwind..Employees Employees
INNER JOIN northwind..Orders Orders ON (Employees.EmployeeID=Orders.EmployeeID) ',
'shipcountry,Year(OrderDate)',
'Count(LastName)[]',
'LastName'
Do it in several steps:
if object_id('tempdb..#Data') is null
CREATE TABLE #Data
([Portfolio] varchar(2), [Date] datetime,
[TotalLoans] decimal(9,2), [ActiveLoans] int, [TotalBalance] decimal(9,2))
;
INSERT INTO #Data
([Portfolio], [Date], [TotalLoans], [ActiveLoans], [TotalBalance])
VALUES
('P1', '2015-12-31', 1000, 900, 100000.00),
('P1', '2015-11-30', 1100, 800, 100100.00),
('P2', '2015-10-31', 1200, 700, 100200.00)
;
WITH Transposed AS (
--First reorganise the data, creating unions like this, by column
--Assumption is that you are not interested in [Portfolio]
SELECT [Portfolio], [Date], [TotalLoans] AS Amount, 'TotalLoans' Field FROM #Data
UNION SELECT [Portfolio], [Date], [ActiveLoans], 'ActiveLoans' FROM #Data
UNION SELECT [Portfolio], [Date], [TotalBalance], 'TotalBalance' FROM #Data
)
SELECT Field, [2015-10-31], [2015-11-30], [2015-12-31] --You can build a string with all the dates from your original data source
FROM (
SELECT [Date], [Field], [Amount] FROM Transposed
) d
PIVOT (
MAX(Amount)
FOR [Date] IN ([2015-10-31], [2015-11-30], [2015-12-31])
) p
Related
This is the table I have:
| Scheme Code | MonthYear | Revenue | Revenue2 |
|-------------|-----------|---------|----------|
| 18VDA | 2018.1 | 100 | 50 |
| 18VDA | 2018.2 | 200 | 100 |
| 18VDA | 2018.3 | 200 | 150 |
and I want to pivot it to like this:
| Scheme Code | 2018.1 A | 2018.2 A | 2018.3 A | 2018.1 B | 2018.2 B | 2018.3 B |
|-------------|----------|----------|----------|----------|----------|----------|
| 18VDA | 100 | 200 | 200 | 50 | 100 | 150 |
How do I do it so that it pivots in MonthYear, but it duplicates it for both Revenue and Revenue2?
Thanks
EDIT: Messed up the output table I was hoping for! I've edited the actual output table I want to see!
EDIT 2:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
Select #cols = STUFF((SELECT ',' + QUOTENAME([MonthYear])
from tableA
group by [MonthYear]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT *
FROM ( SELECT [Scheme Code], MonthYear ,[Revenue]
FROM TableA
) a
PIVOT(sum(Revenue) for MonthYear in (' + #cols + ')
) as RevenueMonth
ORDER BY [Scheme Code]'
execute(#query);
This code I wrote will do it for just one column, and I get the output like this:
| Scheme Code | 2018.1 | 2018.2 | 2018.3 |
|-------------|--------|--------|--------|
| 18VDA | 100 | 200 | 200 |
My suggestion always is to try to write your query as a hard-coded or static version first before diving into dynamic SQL. This let's you get the final result you want with a smaller subset of data and you can verify that you have the logic correct.
I would tackle this by performing an UNPIVOT of the two Revenue columns first, then look at applying the PIVOT function. To UNPIVOT you can use either the UNPIVOT function or you can use CROSS APPLY with a UNION ALL to convert your two Revenue columns into a single column. A static version of the query would be similar to this:
select *
from
(
select
t.[Scheme Code],
new_colname = concat(t.[MonthYear], ' ', r.colname),
r.colvalue
from yourtable t
cross apply
(
select 'A', Revenue union all
select 'B', Revenue2
) r (colname, colvalue)
) d
pivot
(
sum(colvalue)
for new_colname in ([2018.1 A], [2018.2 A], [2018.3 A], [2018.1 B], [2018.2 B], [2018.3 B])
) p;
You'll notice that in the CROSS APPLY I added a column with the A or B that I use to identify either the Revenue or Revenue2 columns. This is then used to create the new column names for the PIVOT.
This should generate the result you want. Now to do this dynamically, you just need to convert the SQL to dynamic code. You can use the following to get the result:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
Select #cols = STUFF((SELECT ',' + QUOTENAME(concat([MonthYear], x.col))
from yourtable
cross join (select col = ' A' union all select ' B') x
group by [MonthYear], x.col
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT *
FROM
(
select
t.[Scheme Code],
new_colname = concat(t.[MonthYear], '' '', r.colname),
r.colvalue
from yourtable t
cross apply
(
select ''A'', Revenue union all
select ''B'', Revenue2
) r (colname, colvalue)
) a
PIVOT
(
sum(colvalue) for new_colname in (' + #cols + ')
) as x
ORDER BY [Scheme Code]';
exec sp_executesql #query;
Both of these should generate the same results (dbfiddle demo)
Do it with CASE and dynamic sql.
DECLARE #colsA AS NVARCHAR(MAX),
#colsB AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #colsA = (SELECT ', sum(case [MonthYear] when ''' + [MonthYear] + ''' then Revenue end)' + QUOTENAME([MonthYear] + ' A')
from tableA
group by [MonthYear]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'),
#colsB = (SELECT ', sum(case [MonthYear] when ''' + [MonthYear] + ''' then Revenue2 end)' + QUOTENAME([MonthYear] + ' B')
from tableA
group by [MonthYear]
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)');
Set #query = 'select [Scheme Code]' + #colsA + #colsB + ' from TableA group by [Scheme Code] order by [Scheme Code];';
print #query;
Very new to SQL Server, at this time I want to use a query with a Pivot table but something is bat, Is there some one could be help me to find my error I appreciate. Regards In the first part I create a dummy table called DateTemp in the second part, I join two tables the DateTemp and datcpc tables, now I want to use a pivot table, but I get an error near to pivot section.
My code:
// First Part
DECLARE #StartDate datetime
DECLARE #EndDate datetime
SELECT #StartDate = '2018-01-01', #EndDate = '2018-11-08'
SELECT #StartDate = #StartDate - (DATEPART(DD, #StartDate) - 1)
DECLARE #DateTemp TABLE (TheDate DATETIME)
WHILE (#StartDate <= #EndDate)
BEGIN
INSERT INTO #DateTemp
VALUES (#StartDate)
SELECT #StartDate = DATEADD(DAY, 1, #StartDate)
END
// Second part
SELECT
CYALA, CYALB
FROM
(SELECT DISTINCT
fechab 'FECHA', clapla 'CLAVEPLANTA', clapro 'CLAVE PRODUCTO',
CAST(SUM(cantid) AS NUMERIC(9, 2)) 'ACTIVIDAD'
FROM
(datcpc
LEFT JOIN
(SELECT TheDate
FROM #DateTemp
WHERE TheDate >= '05/01/2018') NT ON datcpc.fechab = NT.TheDate)
WHERE
datcpc.fechab >= '01/05/2018'
AND datcpc.fechab >= '01/05/2018'
AND (clapla = 'CYALA' OR clapla = 'CYALB' OR clapla = 'CYAZC')
AND datcpc.tipflu = 'C'
GROUP BY
fechab, clapla, clapro) AS SOURCE
PIVOT
(SUM(SOURCE.ACTIVIDAD)
FOR SOURCE.CLAVEPLANTA IN (CYALA, CYALB)
) AS PIVOTABLE
Test the suggested answers, without success. Try a simpler example and even then the error is set as "System.Data.SqlClient.SqlException: 'Line 1: Incorrect syntax near 'PIVOT'.'", I do not know if it is the server configuration or something about it. I use a connection to the server via Visual Studio 2017 via SqlConnection my new easier codes is listed below
SELECT DISTINCT [CLAVEPLANTA], [ACTIVIDAD]
FROM(
SELECT fechab AS [FECHA], clapla AS [CLAVEPLANTA], CAST(SUM(cantid) AS NUMERIC(9,2) ) AS [ACTIVIDAD] FROM datcpc"
WHERE datcpc.fechab >= '01/05/2018'
AND (clapla='CYALA' OR clapla='CYALB' OR clapla='CYAZC')
GROUP BY fechab, clapla
) AS [SO]
PIVOT(SUM([SO.ACTIVIDAD]) FOR [SO.CLAVEPLANTA] IN ([CYALA], [CYALB])) AS [PVT];
I really apreciate you help
I am going to provide 2 variants of a "dynamic pivot" which I hope will assist:
Some sample data:
create table temp
(
date datetime,
category varchar(3),
amount money
)
insert into temp values ('1/1/2012', 'ABC', 1000.00)
insert into temp values ('2/1/2012', 'DEF', 500.00)
insert into temp values ('2/1/2012', 'GHI', 800.00)
insert into temp values ('2/10/2012', 'DEF', 700.00)
insert into temp values ('3/1/2012', 'ABC', 1100.00)
Now a query that will "pivot" that data into columns per day:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(convert(char(10), c.[date],121))
FROM temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT category, ' + #cols + ' from
(
select convert(char(10), [date],121) [date]
, amount
, category
from temp
) x
pivot
(
sum(amount)
for [date] in (' + #cols + ')
) p '
execute(#query)
The result looks like this:
+----------+------------+------------+------------+------------+
| category | 2012-01-01 | 2012-02-01 | 2012-02-10 | 2012-03-01 |
+----------+------------+------------+------------+------------+
| ABC | 1000,0000 | NULL | NULL | 1100,0000 |
| DEF | NULL | 500,0000 | 700,0000 | NULL |
| GHI | NULL | 800,0000 | NULL | NULL |
+----------+------------+------------+------------+------------+
Now, this query "pivots" category into columns:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols = STUFF((SELECT distinct ',' + QUOTENAME(c.category)
FROM temp c
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = 'SELECT convert(char(10),date,121) date, ' + #cols + ' from
(
select date
, amount
, category
from temp
) x
pivot
(
max(amount)
for category in (' + #cols + ')
) p '
execute(#query)
The result looks like this:
+------------+-----------+----------+----------+
| date | ABC | DEF | GHI |
+------------+-----------+----------+----------+
| 2012-01-01 | 1000,0000 | NULL | NULL |
| 2012-02-01 | NULL | 500,0000 | 800,0000 |
| 2012-02-10 | NULL | 700,0000 | NULL |
| 2012-03-01 | 1100,0000 | NULL | NULL |
+------------+-----------+----------+----------+
see these as a live demo here: 2 variants of TSQL pivot tables
origin of these examples Dynamic SQL PIVOT
some suggestions:
--Second part
SELECT
CYALA,CYALB -- you need to specify more columns here
FROM (
SELECT DISTINCT -- distinct is not needed when grouping, remove this
fechab 'FECHA'
,clapla 'CLAVEPLANTA'
,clapro 'CLAVE PRODUCTO'
,CAST(SUM(cantid) AS numeric(9, 2)) 'ACTIVIDAD'
/* if you are creating the temporal table to get EVERY date
, then #DateTemp should be the base table, and left join the [datcpc] */
FROM (datcpc
LEFT JOIN (
SELECT
TheDate
FROM #DateTemp
WHERE TheDate >= '05/01/2018' -- use YYYMMDD or YYYY-MM-DD consistently
) NT
ON datcpc.fechab = NT.TheDate)
WHERE datcpc.fechab >= '01/05/2018'
AND datcpc.fechab >= '01/05/2018' -- repeated, not needed
AND clapla IN('CYALA','CYALB','CYAZC') -- use IN()
AND datcpc.tipflu = 'C'
GROUP BY
fechab ,clapla ,clapro
) AS SOURCE
PIVOT (
SUM(SOURCE.ACTIVIDAD)
FOR SOURCE.CLAVEPLANTA IN (CYALA, CYALB) -- did you want dates here? (as the pivot columns)
) AS PIVOTABLE
I may not have phrased the question clearly so I'll clarify it here.
I've got a VB.NET system that displays data. To display data, I have the following SQL stored procedure that makes use of pivot:
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(form_column_id) from
GetFormColumns(#formTemplateId) FOR XML PATH(''),
TYPE).value('.', 'NVARCHAR(MAX)') ,1,1,'')
set #query = N' select *
from
(select row_number as Row,fc.form_column_id, fdd.data
from
form_data_h fdh
inner join form_data_d fdd on fdd.form_data_h_id = fdh.form_data_h_id
inner join form_column fc on fc.form_column_id = fdd.form_column_id
inner join column_header c on c.column_header_id=fc.column_header_id
where fdh.is_active = 1 and fdh.form_data_h_id= ' +
CONVERT(varchar(10),#formDataHId) + ' and fc.is_active = 1
) src
pivot(
min(data)
for form_column_id in (' + #cols + N')
) piv'
execute(#query);
I had to make use of pivot because user data entry needs to be dynamic. So from this...
table results
The results now look like this.
pivot results
This data has different results filtered by particular parameters. Right now I've displayed the crop data for 2017. I want to join it with the crop data for 2018 (will change based on the Stored Procedure parameter #formDataHId).
That should result in something that looks like this...
2017 and 2018 results
Is there any way I can go about this in SQL or do I have to do that in VB.net? If so, how can I go about it?
Any ideas would be welcome because I'm a bit stumped right now. If users need to see data from let's say 2016 to 2019, it should be presented like that.
Pivot in SQL should be enough. If the structure of data in each year is the same, you can use UNION ALL between each year's data. Then use your query to pivot the combined years' data.
In my pivot function I use a table named Test to generate the pivot. I have 2 other tables Source2017 and Source2018. I insert both Source2017 and Source2018 using this query.
INSERT #Test
SELECT Date, Item, Quantity FROM #Source2017
UNION ALL
SELECT Date, Item, Quantity FROM #Source2018
If I only want data from 2017, I remove the Source2018 from the insert statement.
INSERT #Test
SELECT Date, Item, Quantity FROM #Source2017
Let's say I have more tables, Source2015 and Source2016. If I want to pivot all of them, just add the tables using UNION ALL.
INSERT #Test
SELECT Date, Item, Quantity FROM #Source2015
UNION ALL
SELECT Date, Item, Quantity FROM #Source2016
UNION ALL
SELECT Date, Item, Quantity FROM #Source2017
UNION ALL
SELECT Date, Item, Quantity FROM #Source2018
Full query:
IF OBJECT_ID('tempdb..#Test') IS NOT NUll DROP TABLE #Test
IF OBJECT_ID('tempdb..#Source2017') IS NOT NUll DROP TABLE #Source2017
IF OBJECT_ID('tempdb..#Source2018') IS NOT NUll DROP TABLE #Source2018
CREATE TABLE #Test
(
Date DATE,
Item VARCHAR(100),
Quantity INT
)
CREATE TABLE #Source2017
(
Date DATE,
Item VARCHAR(100),
Quantity INT
)
CREATE TABLE #Source2018
(
Date DATE,
Item VARCHAR(100),
Quantity INT
)
INSERT #Source2017 VALUES
('2017/01/01', 'Mango', 5),
('2017/01/01', 'Orange', 6),
('2017/01/02', 'Mango', 7),
('2017/01/02', 'Orange', 8),
('2017/01/02', 'Cherry', 9)
INSERT #Source2018 VALUES
('2018/01/01', 'Durian', 15),
('2018/01/02', 'Orange', 28),
('2018/01/03', 'Cherry', 19)
INSERT #Test
SELECT Date, Item, Quantity FROM #Source2017
UNION ALL
SELECT Date, Item, Quantity FROM #Source2018
DECLARE #SQL AS VARCHAR(MAX)
DECLARE #Columns AS VARCHAR(MAX)
DECLARE #Columns2 AS VARCHAR(MAX)
SELECT #Columns = COALESCE(#Columns + ',','') + QUOTENAME(Date)
FROM (SELECT DISTINCT Date FROM #Test) AS B
ORDER BY B.Date
SELECT #Columns2 = COALESCE(#Columns2 + ',','') + 'ISNULL(' + QUOTENAME(Date) + ', 0) AS [' + CAST(Date AS VARCHAR(100)) + ']'
FROM (SELECT DISTINCT Date FROM #Test) AS B
ORDER BY B.Date
SET #SQL = '
WITH PivotData AS
(
SELECT Date, Item, Quantity FROM #Test
)
SELECT
Item, ' + #Columns2 + '
FROM PivotData
PIVOT
(
SUM(Quantity)
FOR Date
IN (' + #Columns + ')
) AS PivotResult
ORDER BY Item'
EXEC(#SQL);
DROP TABLE #Test
DROP TABLE #Source2017
DROP TABLE #Source2018
Result:
+--------+------------+------------+------------+------------+------------+
| Item | 2017-01-01 | 2017-01-02 | 2018-01-01 | 2018-01-02 | 2018-01-03 |
+--------+------------+------------+------------+------------+------------+
| Cherry | 0 | 9 | 0 | 0 | 19 |
| Durian | 0 | 0 | 15 | 0 | 0 |
| Mango | 5 | 7 | 0 | 0 | 0 |
| Orange | 6 | 8 | 0 | 28 | 0 |
+--------+------------+------------+------------+------------+------------+
I have a table which has events and the day they occurred:
Table 'Events':
Name Day
-----------
A 1
B 2
A 2
B 3
I need output columns to be a date range based on query input with rows being which event happened on the day so:
Desired output:
Day-1 Day-2 Day-3
-----------------
A A -
- B B
If this is possible can anyone give me a sample query that could generate this output based on a date range. There are all sorts of I have no clue how to even approach this issues here like an unknown number of columns.
You can use conditional aggregation:
select max(case when day = 1 then name end) as day_1,
max(case when day = 2 then name end) as day_2,
max(case when day = 3 then name end) as day_3
from t
group by name;
Note: This returns NULL rather than -. I think NULL makes more sense.
Try this one...
Table Script and Sample data
CREATE TABLE [TableName](
[Name] [nvarchar](50) NULL,
[Day] [int] NULL
)
INSERT [TableName] ([Name], [Day]) VALUES (N'A', 1)
INSERT [TableName] ([Name], [Day]) VALUES (N'B', 2)
INSERT [TableName] ([Name], [Day]) VALUES (N'A', 2)
INSERT [TableName] ([Name], [Day]) VALUES (N'B', 3)
Query (dynamic PIVOT)
DECLARE #startDay AS INT;
DECLARE #endDay AS INT;
SET #startDay = 1;
SET #endDay = 3;
DECLARE #cols AS NVARCHAR(max) = Stuff((SELECT DISTINCT ',' + Quotename([day])
FROM TableName
WHERE [Day] >= #startDay AND [Day] <= #endDay
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
DECLARE #query AS NVARCHAR(max) = 'SELECT '+ #cols +'
FROM (SELECT *,
Dense_rank() OVER (ORDER BY NAME) AS dr
FROM TableName) sq
PIVOT(Max([name])
FOR [day] IN ('+ #cols +') ) pvt ';
EXECUTE(#query)
Output
+--------+---+--------+
| 1 | 2 | 3 |
+--------+---+--------+
| A | A | (null) |
| (null) | B | B |
+--------+---+--------+
Online Demo: http://www.sqlfiddle.com/#!18/c688b/8/0
If you also want to use custom column name, try this...
DECLARE #startDay AS INT;
DECLARE #endDay AS INT;
SET #startDay = 1;
SET #endDay = 3;
DECLARE #cols AS NVARCHAR(max) = Stuff((SELECT DISTINCT ',' + Quotename([day])
FROM TableName
WHERE [Day] >= #startDay AND [Day] <= #endDay
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
DECLARE #colNames AS NVARCHAR(max) = Stuff((SELECT DISTINCT ',' + Quotename([day]) + ' AS Days' + CONVERT(NVARCHAR(MAX), [day])
FROM TableName
WHERE [Day] >= #startDay AND [Day] <= #endDay
FOR xml path(''), type).value('.', 'NVARCHAR(MAX)'), 1, 1, '');
DECLARE #query AS NVARCHAR(max) = 'SELECT '+ #colNames +'
FROM (SELECT *,
Dense_rank() OVER (ORDER BY NAME) AS dr
FROM TableName) sq
PIVOT(Max([name])
FOR [day] IN ('+ #cols +') ) pvt ';
EXECUTE(#query)
Output
+-------+-------+-------+
| Days1 | Days2 | Days3 |
+-------+-------+-------+
| A | A | NULL |
| NULL | B | B |
+-------+-------+-------+
Online Demo: http://www.sqlfiddle.com/#!18/c688b/9/0
This question already has answers here:
Efficiently convert rows to columns in sql server
(5 answers)
Closed 8 years ago.
i have the following table
create table mytab (
mID int primary key,
pname varchar(100) not null,
pvalue varchar(100) not null
)
example data looks like
mID |pname |pvalue
-----------------------
1 |AAR | 2.3
1 |AAM | 1.2
1 |GXX | 5
2 |AAR | 5.4
2 |AAM | 3.0
3 |AAR | 0.2
I want to flip the table so that i get
mID | AAR | AAM | GXX|
---------------------------------
1 | 2.3 | 1.2 | 5|
2 | 5.4 | 3.0 | 0|
3 | 0.2 | 0 | 0
Is this somehow possible and if so, is there a way to create a dynamic query because there are lots of these pname pvalue pairs
Write Dynamic Pivot Query as:
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
DECLARE #colsFinal AS NVARCHAR(MAX)
select #cols = STUFF((SELECT distinct ',' +
QUOTENAME(pname)
FROM mytab
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
select #colsFinal = STUFF((SELECT distinct ',' +
'ISNULL('+QUOTENAME(pname)+',0) AS '+ QUOTENAME(pname)
FROM mytab
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
--Edited query to replace null with 0 in Final result set.
SELECT #query = 'SELECT mID, '+#colsFinal +'
FROM mytab
PIVOT
(
MAX(pvalue)
FOR pname IN(' + #cols + ')) AS p;'
exec sp_executesql #query
Check demo here..
Use pivot as following format:
select *
From (select *
From mytab)p
PIVOT(SUM(pvalue) FOR pname IN ([AAR],[AAM],[GXX]))pvt
In order to dynamic PIVOT use flowing reference:
Dynamic PIVOT Sample1
Dynamic PIVOT Sample2
Declare #t table (mID INT, pname VARCHAR(10), pvalue FLOAT)
INSERT INTO #t (mID,pname,pvalue)values (1,'AAR',2.3)
INSERT INTO #t (mID,pname,pvalue)values (1,'AAM', 1.2)
INSERT INTO #t (mID,pname,pvalue)values (1,'GXX', 5)
INSERT INTO #t (mID,pname,pvalue)values (2,'AAR', 5.4)
INSERT INTO #t (mID,pname,pvalue)values (2,'AAM', 0.3)
INSERT INTO #t (mID,pname,pvalue)values (3,'AAR', 0.2)
select mid,
CASE WHEN [AAR]IS NOT NULL THEN [AAR] ELSE ISNULL([AAR],0)END [AAR],
CASE WHEN [AAM]IS NOT NULL THEN [AAM] ELSE ISNULL([AAM],0)END [AAM],
CASE WHEN [GXX]IS NOT NULL THEN [GXX] ELSE ISNULL([GXX],0)END [GXX]
from
(
select mID, pvalue,pname
from #t
) d
pivot
(
max(pvalue)
for pname in ( [AAR], [AAM], [GXX])
) piv;