SQL Server dynamic pivot with multiple columns - sql-server

Here's the scenario I am in. I have my data in the following format.
My source data
IssuedOn Country Sales Transactions
------------------------------------------
29-Aug-16 India 40 8
29-Aug-16 Australia 15 3
29-Aug-16 Canada 15 3
30-Aug-16 India 50 10
30-Aug-16 Australia 25 5
30-Aug-16 Canada 10 2
31-Aug-16 India 100 25
31-Aug-16 Australia 30 10
31-Aug-16 Canada 55 12
This is the output I am looking for
Expected output
IssuedDate Australia Canada India TotalSales Transactionscount
---------------------------------------------------------------------
29-Aug-16 15 15 40 70 14
30-Aug-16 25 10 50 85 17
31-Aug-16 30 55 100 185 47
I have been able to pivot the data on country and get the "Total Sales" column. However, I am not able to get the "Total Transactions" column right.
Here's the code to generate the source data table. Would really help if someone can guide me.
Create Table tbl1
(
IssuedOn date,
Country varchar(100),
Sales bigint,
Transactions bigint
)
Insert into tbl1(IssuedOn, Country, Sales, Transactions)
Values ('2016-08-29', 'India', 40, 8),
('2016-08-29', 'Australia', 15, 3),
('2016-08-29', 'Canada', 15, 3),
('2016-08-30', 'India', 50, 10),
('2016-08-30', 'Australia', 25, 5),
('2016-08-30', 'Canada', 10, 2),
('2016-08-31', 'India', 100, 25),
('2016-08-31', 'Australia', 30, 10),
('2016-08-31', 'Canada', 55, 12)
select *
from tbl1

A more verbose dynamic SQL query, without the use of a stored procedure is as follows:
DECLARE #cols AS NVARCHAR(MAX);
DECLARE #pivotSales AS NVARCHAR(MAX);
DECLARE #sql AS NVARCHAR(MAX);
SELECT #cols = ISNULL(#cols + ', ', '') + QUOTENAME(Country)
FROM (SELECT DISTINCT Country FROM tbl1) AS Countries
SET #pivotSales = N'SELECT IssuedOn, ' + #cols +'
FROM (SELECT IssuedOn, Country, Sales FROM tbl1) AS sales
PIVOT(SUM(Sales) FOR Country IN (' + #cols + ')) AS pvt';
SET #sql = ';WITH CTE_SalesPivot AS (
'+#pivotSales+'
),
CTE_SalesTotal AS (
SELECT IssuedOn, SUM(Sales) AS [Grand Total]
FROM tbl1
GROUP BY IssuedOn
),
CTE_Transactions AS (
SELECT IssuedOn, SUM(Transactions) AS [Transaction Count]
FROM tbl1
GROUP BY IssuedOn
)
SELECT CTE_SalesPivot.IssuedOn, ' + #cols + ', CTE_SalesTotal.[Grand Total], CTE_Transactions.[Transaction Count]
FROM
CTE_SalesPivot
INNER JOIN CTE_SalesTotal ON CTE_SalesPivot.IssuedOn = CTE_SalesTotal.IssuedOn
INNER JOIN CTE_Transactions ON CTE_SalesPivot.IssuedOn = CTE_Transactions.IssuedOn';
EXEC sp_executesql #sql;

The following is the stored procedure used for the bulk of my dynamic pivots
Exec [prc-Pivot] 'tbl1','Country','sum(Sales)[]','IssuedOn','sum(Transactions)[Transactions],sum(Sales)[TotalSales]'
IssuedOn Transactions TotalSales Australia Canada India
2016-08-29 14 70 15 15 40
2016-08-30 17 85 25 10 50
2016-08-31 47 185 30 55 100
The stored procedure
ALTER PROCEDURE [dbo].[prc-Pivot] (
#Source varchar(1000), -- Any Table or Select Statement
#PvotCol varchar(250), -- Field name or expression ie. Month(Date)
#Summaries varchar(250), -- aggfunction(aggValue)[optionalTitle]
#GroupBy varchar(250), -- Optional additional Group By
#OtherCols varchar(500) ) -- Optional Group By or aggregates
AS
--Exec [prc-Pivot] 'Select Year=Year(TR_Date),* From [Chinrus-Series].[dbo].[DS_Treasury_Rates]','''Q''+DateName(QQ,TR_Date)','avg(TR_Y10)[-Avg]','Year','count(*)[Records],min(TR_Y10)[Min],max(TR_Y10)[Max],Avg(TR_Y10)[Avg]'
--Exec [prc-Pivot] '#Temp','Attribute','max(Description)[]','ID','count(*)[Records]'
Set NoCount On
Declare #Vals varchar(max),#SQL varchar(max);
Set #Vals = ''
Set #OtherCols= IsNull(', ' + #OtherCols,'')
Set #Source = case when #Source Like 'Select%' then #Source else 'Select * From '+#Source end
Create Table #TempPvot (Pvot varchar(100))
Insert Into #TempPvot
Exec ('Select Distinct Convert(varchar(100),' + #PvotCol + ') as Pvot FROM (' + #Source + ') A')
--Select #Vals = #Vals + ', isnull(' + Replace(Replace(#Summaries,'(','(CASE WHEN ' + #PvotCol + '=''' + Pvot + ''' THEN '),')[', ' END),NULL) As [' + Pvot ) From #TempPvot Order by Pvot
Select #Vals = #Vals + ', isnull(' + Replace(Replace(#Summaries,'(','(CASE WHEN ' + #PvotCol + '=''' + Pvot + ''' THEN '),')[', ' END),0) As [' + Pvot ) From #TempPvot Order by Pvot
Drop Table #TempPvot
Set #SQL = Replace('Select ' + Isnull(#GroupBy,'') + #OtherCols + #Vals + ' From (' + #Source + ') PvtFinal ' + case when Isnull(#GroupBy,'')<>'' then 'Group By ' + #GroupBy + ' Order by ' + #GroupBy else '' end,'Select , ','Select ')
--Print #SQL
Exec (#SQL)

here's a simple way to do it using aggregate case expressions.
DECLARE #cols NVARCHAR(MAX),
#sql NVARCHAR(MAX)
SELECT #cols = STUFF((
SELECT ',' + 'SUM(CASE WHEN Country = ''' + Country + ''' THEN Sales END) AS ' + QUOTENAME(Country)
FROM (SELECT DISTINCT Country FROM tbl1) t
ORDER BY Country
FOR XML PATH('')
),1, 1, '')
SET #sql = N'
SELECT IssuedOn, ' + #cols + ',
SUM(Sales) AS TotalSales,
SUM(Transactions) AS TransactionCount
FROM tbl1
GROUP BY IssuedOn
'
EXEC(#sql)
this will generate a query that should look like this.
SELECT IssuedOn,
SUM(CASE WHEN Country = 'Australia' THEN Sales END) AS [Australia],
SUM(CASE WHEN Country = 'Canada' THEN Sales END) AS [Canada],
SUM(CASE WHEN Country = 'India' THEN Sales END) AS [India],
SUM(Sales) AS TotalSales,
SUM(Transactions) AS TransactionCount
FROM tbl1
GROUP BY IssuedOn

Related

How to pivot columns in SQL Server

I'm new to SQL Server and I am trying to pivot rows into columns
select SalesOrExpense, store_no, total
from myTable
SalesOrExpense Store_No total ($)
-------------------------------------
Expense 22 100
Sales 22 400
to look like this
Store_No Expense Sales
---------------------------------
22 100 400
Could someone help point me in the right direction?
Thanks
We can even do it by executing dynamic sql query..
Query
declare #sql as varchar(max);
select #sql = stuff((
select ', sum(case [SalesOrExpense] when ' + char(39) +
[SalesOrExpense] + char(39) +
' then [total] else 0 end) as [' + [SalesOrExpense] + ']'
from [your_table_name]
for xml path('')
)
, 1, 2, ''
);
select #sql = 'select [Store_No], ' + #sql +
' from [your_table_name] group by [Store_No];';
exec(#sql);
CREATE TABLE #Table1
([SalesOrExpense] varchar(7), [Store_No] int, [total] int)
;
INSERT INTO #Table1
([SalesOrExpense], [Store_No], [total])
VALUES
('Expense', 22, 100),
('Sales', 22, 400)
select *
from
(
select *
from #Table1
) src
pivot
(
max(total)
for SalesOrExpense in ([Expense], [Sales])
) piv;
CREATE TABLE #Table1
([SalesOrExpense] varchar(7), [Store_No] int, [total] int);
INSERT INTO #Table1
([SalesOrExpense], [Store_No], [total])
VALUES
('Expense', 22, 100),
('Sales', 22, 400);
SELECT Store_No,
SUM(CASE WHEN SalesOrExpense='Expense' THEN TOTAL END) AS Expense,
SUM(CASE WHEN SalesOrExpense='Sales' THEN TOTAL END) AS Sales
FROM #table1 GROUP BY Store_No;
OUTPUT:-
-------------------------
Store_No | Expense |Sales
---------------------------
22 100 400

Query to retrieve column values as rows from table which has duplicate rows in SQL Server 2008

I have a table which has records in the following format:
OrderID | CategoryID | Percentage
-----------------------------------------
Order1 1 50
Order1 2 100
Order1 3 70
Order2 1 85
Order2 2 75
Order2 3 100
Order3 1 50
Order3 2 60
Order3 3 80
Each order has multiple categories and each category has a percentage corresponding to that order.
I want to display it in the following format:
OrderID | Category1_% | Category2_% | Category3_%
-------------------------------------------------------
Order1 50 100 70
Order2 85 75 100
Order3 50 60 80
How do I write a query in SQL Server 2008 to retrieve percentages for each category of each (distinct) order? Please help.
Edit:
Orders table
ID | StartDate
--------------------
Order1 1/1/2016
Order2 10/1/2015
Order3 10/5/2016
The query which I executed is:
Declare #SQL varchar(max)
Declare #startDate datetime
set #startDate='1/1/2015'
Declare #endDate datetime
set #endDate= '10/1/2016'
Select #SQL = Stuff((Select Distinct ',' + QuoteName('Category'+cast(CategoryID as varchar(25))+'_%') From OrdersData Order By 1 For XML Path('')),1,1,'')
Select #SQL = 'Select [OrderID],' + #SQL +'
From (Select OrderID,Percentage,Item=''Category''+cast(CategoryID as varchar(25))+''_%'' from OrdersData join Orders on Orders.ID =OrdersData.OrderID
WHERE Orders.StartDate >= '+ ***convert(nvarchar(25), #startDate, 121)*** + ' and GCWR.StartDate <= '+ ***convert(nvarchar(25), #endDate, 121)*** +') A
Pivot (max(Percentage) For Item in (' + #SQL + ') ) p'
Exec(#SQL);
When I execute, I get an error 'Incorrect syntax near '00'.' How do I call the datetime values in the above query to display the results? Please let me know.
Just in case you need to go dynamic
Declare #SQL varchar(max)
Select #SQL = Stuff((Select Distinct ',' + QuoteName('Category'+cast(CategoryID as varchar(25))+'_%') From YourTable Order By 1 For XML Path('')),1,1,'')
Select #SQL = 'Select [OrderID],' + #SQL +'
From (Select OrderID,Percentage,Item=''Category''+cast(CategoryID as varchar(25))+''_%'' from YourTable) A
Pivot (max(Percentage) For Item in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
Edit - Updated with StartDate
Declare #Date1 datetime = '2015-01-01'
Declare #Date2 datetime = '2016-10-01'
Declare #SQL varchar(max)
Select #SQL = Stuff((Select Distinct ',' + QuoteName('Category'+cast(CategoryID as varchar(25))+'_%') From YourTable Where cast(StartDate as date) between #Date1 and #Date2 Order By 1 For XML Path('')),1,1,'')
Select #SQL = 'Select [OrderID],' + #SQL +'
From (Select OrderID,Percentage,Item=''Category''+cast(CategoryID as varchar(25))+''_%'' from YourTable Where cast(StartDate as date) between '''+convert(varchar(10),#Date1,120)+''' and '''+convert(varchar(10),#Date2,120)+''') A
Pivot (max(Percentage) For Item in (' + #SQL + ') ) p'
Exec(#SQL);
Returns
Give this a try:
Select OrderID, [1] as [Category1_%], [2] as [Category2_%],[3] as [Category3_%]
From [TableName]
PIVOT (MAX([Percentage]) FOR [CategoryID] IN ([1],[2],[3])) myPivot
This assumes that you know all the possible values in the categoryID field and that each CategoryID an OrderID combination has a single percentage value.

Pivot in sql server returns error "incorrect syntax near the keyword 'for': The DUPLICATE has not the answer [duplicate]

I'm looking for an efficient way to convert rows to columns in SQL server, I heard that PIVOT is not very fast, and I need to deal with lot of records.
This is my example:
-------------------------------
| Id | Value | ColumnName |
-------------------------------
| 1 | John | FirstName |
| 2 | 2.4 | Amount |
| 3 | ZH1E4A | PostalCode |
| 4 | Fork | LastName |
| 5 | 857685 | AccountNumber |
-------------------------------
This is my result:
---------------------------------------------------------------------
| FirstName |Amount| PostalCode | LastName | AccountNumber |
---------------------------------------------------------------------
| John | 2.4 | ZH1E4A | Fork | 857685 |
---------------------------------------------------------------------
How can I build the result?
There are several ways that you can transform data from multiple rows into columns.
Using PIVOT
In SQL Server you can use the PIVOT function to transform the data from rows to columns:
select Firstname, Amount, PostalCode, LastName, AccountNumber
from
(
select value, columnname
from yourtable
) d
pivot
(
max(value)
for columnname in (Firstname, Amount, PostalCode, LastName, AccountNumber)
) piv;
See Demo.
Pivot with unknown number of columnnames
If you have an unknown number of columnnames that you want to transpose, then you can use dynamic SQL:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(ColumnName)
from yourtable
group by ColumnName, id
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT ' + #cols + N' from
(
select value, ColumnName
from yourtable
) x
pivot
(
max(value)
for ColumnName in (' + #cols + N')
) p '
exec sp_executesql #query;
See Demo.
Using an aggregate function
If you do not want to use the PIVOT function, then you can use an aggregate function with a CASE expression:
select
max(case when columnname = 'FirstName' then value end) Firstname,
max(case when columnname = 'Amount' then value end) Amount,
max(case when columnname = 'PostalCode' then value end) PostalCode,
max(case when columnname = 'LastName' then value end) LastName,
max(case when columnname = 'AccountNumber' then value end) AccountNumber
from yourtable
See Demo.
Using multiple joins
This could also be completed using multiple joins, but you will need some column to associate each of the rows which you do not have in your sample data. But the basic syntax would be:
select fn.value as FirstName,
a.value as Amount,
pc.value as PostalCode,
ln.value as LastName,
an.value as AccountNumber
from yourtable fn
left join yourtable a
on fn.somecol = a.somecol
and a.columnname = 'Amount'
left join yourtable pc
on fn.somecol = pc.somecol
and pc.columnname = 'PostalCode'
left join yourtable ln
on fn.somecol = ln.somecol
and ln.columnname = 'LastName'
left join yourtable an
on fn.somecol = an.somecol
and an.columnname = 'AccountNumber'
where fn.columnname = 'Firstname'
This is rather a method than just a single script but gives you much more flexibility.
First of all There are 3 objects:
User defined TABLE type [ColumnActionList] -> holds data as
parameter
SP [proc_PivotPrepare] -> prepares our data
SP [proc_PivotExecute] -> execute the script
CREATE TYPE [dbo].[ColumnActionList] AS TABLE
(
[ID] [smallint] NOT NULL,
[ColumnName] nvarchar NOT NULL,
[Action] nchar NOT NULL
);
GO
CREATE PROCEDURE [dbo].[proc_PivotPrepare]
(
#DB_Name nvarchar(128),
#TableName nvarchar(128)
)
AS
SELECT #DB_Name = ISNULL(#DB_Name,db_name())
DECLARE #SQL_Code nvarchar(max)
DECLARE #MyTab TABLE (ID smallint identity(1,1), [Column_Name] nvarchar(128), [Type] nchar(1), [Set Action SQL] nvarchar(max));
SELECT #SQL_Code = 'SELECT [<| SQL_Code |>] = '' '' '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''-----| Declare user defined type [ID] / [ColumnName] / [PivotAction] '' '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''DECLARE #ColumnListWithActions ColumnActionList;'''
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''-----| Set [PivotAction] (''''S'''' as default) to select dimentions and values '' '
+ 'UNION ALL '
+ 'SELECT ''-----|'''
+ 'UNION ALL '
+ 'SELECT ''-----| ''''S'''' = Stable column || ''''D'''' = Dimention column || ''''V'''' = Value column '' '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''INSERT INTO #ColumnListWithActions VALUES ('' + CAST( ROW_NUMBER() OVER (ORDER BY [NAME]) as nvarchar(10)) + '', '' + '''''''' + [NAME] + ''''''''+ '', ''''S'''');'''
+ 'FROM [' + #DB_Name + '].sys.columns '
+ 'WHERE object_id = object_id(''[' + #DB_Name + ']..[' + #TableName + ']'') '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''-----| Execute sp_PivotExecute with parameters: columns and dimentions and main table name'' '
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
+ 'UNION ALL '
+ 'SELECT ''EXEC [dbo].[sp_PivotExecute] #ColumnListWithActions, ' + '''''' + #TableName + '''''' + ';'''
+ 'UNION ALL '
+ 'SELECT ''----------------------------------------------------------------------------------------------------'' '
EXECUTE SP_EXECUTESQL #SQL_Code;
GO
CREATE PROCEDURE [dbo].[sp_PivotExecute]
(
#ColumnListWithActions ColumnActionList ReadOnly
,#TableName nvarchar(128)
)
AS
--#######################################################################################################################
--###| Step 1 - Select our user-defined-table-variable into temp table
--#######################################################################################################################
IF OBJECT_ID('tempdb.dbo.#ColumnListWithActions', 'U') IS NOT NULL DROP TABLE #ColumnListWithActions;
SELECT * INTO #ColumnListWithActions FROM #ColumnListWithActions;
--#######################################################################################################################
--###| Step 2 - Preparing lists of column groups as strings:
--#######################################################################################################################
DECLARE #ColumnName nvarchar(128)
DECLARE #Destiny nchar(1)
DECLARE #ListOfColumns_Stable nvarchar(max)
DECLARE #ListOfColumns_Dimension nvarchar(max)
DECLARE #ListOfColumns_Variable nvarchar(max)
--############################
--###| Cursor for List of Stable Columns
--############################
DECLARE ColumnListStringCreator_S CURSOR FOR
SELECT [ColumnName]
FROM #ColumnListWithActions
WHERE [Action] = 'S'
OPEN ColumnListStringCreator_S;
FETCH NEXT FROM ColumnListStringCreator_S
INTO #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #ListOfColumns_Stable = ISNULL(#ListOfColumns_Stable, '') + ' [' + #ColumnName + '] ,';
FETCH NEXT FROM ColumnListStringCreator_S INTO #ColumnName
END
CLOSE ColumnListStringCreator_S;
DEALLOCATE ColumnListStringCreator_S;
--############################
--###| Cursor for List of Dimension Columns
--############################
DECLARE ColumnListStringCreator_D CURSOR FOR
SELECT [ColumnName]
FROM #ColumnListWithActions
WHERE [Action] = 'D'
OPEN ColumnListStringCreator_D;
FETCH NEXT FROM ColumnListStringCreator_D
INTO #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #ListOfColumns_Dimension = ISNULL(#ListOfColumns_Dimension, '') + ' [' + #ColumnName + '] ,';
FETCH NEXT FROM ColumnListStringCreator_D INTO #ColumnName
END
CLOSE ColumnListStringCreator_D;
DEALLOCATE ColumnListStringCreator_D;
--############################
--###| Cursor for List of Variable Columns
--############################
DECLARE ColumnListStringCreator_V CURSOR FOR
SELECT [ColumnName]
FROM #ColumnListWithActions
WHERE [Action] = 'V'
OPEN ColumnListStringCreator_V;
FETCH NEXT FROM ColumnListStringCreator_V
INTO #ColumnName
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #ListOfColumns_Variable = ISNULL(#ListOfColumns_Variable, '') + ' [' + #ColumnName + '] ,';
FETCH NEXT FROM ColumnListStringCreator_V INTO #ColumnName
END
CLOSE ColumnListStringCreator_V;
DEALLOCATE ColumnListStringCreator_V;
SELECT #ListOfColumns_Variable = LEFT(#ListOfColumns_Variable, LEN(#ListOfColumns_Variable) - 1);
SELECT #ListOfColumns_Dimension = LEFT(#ListOfColumns_Dimension, LEN(#ListOfColumns_Dimension) - 1);
SELECT #ListOfColumns_Stable = LEFT(#ListOfColumns_Stable, LEN(#ListOfColumns_Stable) - 1);
--#######################################################################################################################
--###| Step 3 - Preparing table with all possible connections between Dimension columns excluding NULLs
--#######################################################################################################################
DECLARE #DIM_TAB TABLE ([DIM_ID] smallint, [ColumnName] nvarchar(128))
INSERT INTO #DIM_TAB
SELECT [DIM_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName] FROM #ColumnListWithActions WHERE [Action] = 'D';
DECLARE #DIM_ID smallint;
SELECT #DIM_ID = 1;
DECLARE #SQL_Dimentions nvarchar(max);
IF OBJECT_ID('tempdb.dbo.##ALL_Dimentions', 'U') IS NOT NULL DROP TABLE ##ALL_Dimentions;
SELECT #SQL_Dimentions = 'SELECT [xxx_ID_xxx] = ROW_NUMBER() OVER (ORDER BY ' + #ListOfColumns_Dimension + '), ' + #ListOfColumns_Dimension
+ ' INTO ##ALL_Dimentions '
+ ' FROM (SELECT DISTINCT' + #ListOfColumns_Dimension + ' FROM ' + #TableName
+ ' WHERE ' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = #DIM_ID) + ' IS NOT NULL ';
SELECT #DIM_ID = #DIM_ID + 1;
WHILE #DIM_ID <= (SELECT MAX([DIM_ID]) FROM #DIM_TAB)
BEGIN
SELECT #SQL_Dimentions = #SQL_Dimentions + 'AND ' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = #DIM_ID) + ' IS NOT NULL ';
SELECT #DIM_ID = #DIM_ID + 1;
END
SELECT #SQL_Dimentions = #SQL_Dimentions + ' )x';
EXECUTE SP_EXECUTESQL #SQL_Dimentions;
--#######################################################################################################################
--###| Step 4 - Preparing table with all possible connections between Stable columns excluding NULLs
--#######################################################################################################################
DECLARE #StabPos_TAB TABLE ([StabPos_ID] smallint, [ColumnName] nvarchar(128))
INSERT INTO #StabPos_TAB
SELECT [StabPos_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName] FROM #ColumnListWithActions WHERE [Action] = 'S';
DECLARE #StabPos_ID smallint;
SELECT #StabPos_ID = 1;
DECLARE #SQL_MainStableColumnTable nvarchar(max);
IF OBJECT_ID('tempdb.dbo.##ALL_StableColumns', 'U') IS NOT NULL DROP TABLE ##ALL_StableColumns;
SELECT #SQL_MainStableColumnTable = 'SELECT xxx_ID_xxx = ROW_NUMBER() OVER (ORDER BY ' + #ListOfColumns_Stable + '), ' + #ListOfColumns_Stable
+ ' INTO ##ALL_StableColumns '
+ ' FROM (SELECT DISTINCT' + #ListOfColumns_Stable + ' FROM ' + #TableName
+ ' WHERE ' + (SELECT [ColumnName] FROM #StabPos_TAB WHERE [StabPos_ID] = #StabPos_ID) + ' IS NOT NULL ';
SELECT #StabPos_ID = #StabPos_ID + 1;
WHILE #StabPos_ID <= (SELECT MAX([StabPos_ID]) FROM #StabPos_TAB)
BEGIN
SELECT #SQL_MainStableColumnTable = #SQL_MainStableColumnTable + 'AND ' + (SELECT [ColumnName] FROM #StabPos_TAB WHERE [StabPos_ID] = #StabPos_ID) + ' IS NOT NULL ';
SELECT #StabPos_ID = #StabPos_ID + 1;
END
SELECT #SQL_MainStableColumnTable = #SQL_MainStableColumnTable + ' )x';
EXECUTE SP_EXECUTESQL #SQL_MainStableColumnTable;
--#######################################################################################################################
--###| Step 5 - Preparing table with all options ID
--#######################################################################################################################
DECLARE #FULL_SQL_1 NVARCHAR(MAX)
SELECT #FULL_SQL_1 = ''
DECLARE #i smallint
IF OBJECT_ID('tempdb.dbo.##FinalTab', 'U') IS NOT NULL DROP TABLE ##FinalTab;
SELECT #FULL_SQL_1 = 'SELECT t.*, dim.[xxx_ID_xxx] '
+ ' INTO ##FinalTab '
+ 'FROM ' + #TableName + ' t '
+ 'JOIN ##ALL_Dimentions dim '
+ 'ON t.' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = 1) + ' = dim.' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = 1);
SELECT #i = 2
WHILE #i <= (SELECT MAX([DIM_ID]) FROM #DIM_TAB)
BEGIN
SELECT #FULL_SQL_1 = #FULL_SQL_1 + ' AND t.' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = #i) + ' = dim.' + (SELECT [ColumnName] FROM #DIM_TAB WHERE [DIM_ID] = #i)
SELECT #i = #i +1
END
EXECUTE SP_EXECUTESQL #FULL_SQL_1
--#######################################################################################################################
--###| Step 6 - Selecting final data
--#######################################################################################################################
DECLARE #STAB_TAB TABLE ([STAB_ID] smallint, [ColumnName] nvarchar(128))
INSERT INTO #STAB_TAB
SELECT [STAB_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName]
FROM #ColumnListWithActions WHERE [Action] = 'S';
DECLARE #VAR_TAB TABLE ([VAR_ID] smallint, [ColumnName] nvarchar(128))
INSERT INTO #VAR_TAB
SELECT [VAR_ID] = ROW_NUMBER() OVER(ORDER BY [ColumnName]), [ColumnName]
FROM #ColumnListWithActions WHERE [Action] = 'V';
DECLARE #y smallint;
DECLARE #x smallint;
DECLARE #z smallint;
DECLARE #FinalCode nvarchar(max)
SELECT #FinalCode = ' SELECT ID1.*'
SELECT #y = 1
WHILE #y <= (SELECT MAX([xxx_ID_xxx]) FROM ##FinalTab)
BEGIN
SELECT #z = 1
WHILE #z <= (SELECT MAX([VAR_ID]) FROM #VAR_TAB)
BEGIN
SELECT #FinalCode = #FinalCode + ', [ID' + CAST((#y) as varchar(10)) + '.' + (SELECT [ColumnName] FROM #VAR_TAB WHERE [VAR_ID] = #z) + '] = ID' + CAST((#y + 1) as varchar(10)) + '.' + (SELECT [ColumnName] FROM #VAR_TAB WHERE [VAR_ID] = #z)
SELECT #z = #z + 1
END
SELECT #y = #y + 1
END
SELECT #FinalCode = #FinalCode +
' FROM ( SELECT * FROM ##ALL_StableColumns)ID1';
SELECT #y = 1
WHILE #y <= (SELECT MAX([xxx_ID_xxx]) FROM ##FinalTab)
BEGIN
SELECT #x = 1
SELECT #FinalCode = #FinalCode
+ ' LEFT JOIN (SELECT ' + #ListOfColumns_Stable + ' , ' + #ListOfColumns_Variable
+ ' FROM ##FinalTab WHERE [xxx_ID_xxx] = '
+ CAST(#y as varchar(10)) + ' )ID' + CAST((#y + 1) as varchar(10))
+ ' ON 1 = 1'
WHILE #x <= (SELECT MAX([STAB_ID]) FROM #STAB_TAB)
BEGIN
SELECT #FinalCode = #FinalCode + ' AND ID1.' + (SELECT [ColumnName] FROM #STAB_TAB WHERE [STAB_ID] = #x) + ' = ID' + CAST((#y+1) as varchar(10)) + '.' + (SELECT [ColumnName] FROM #STAB_TAB WHERE [STAB_ID] = #x)
SELECT #x = #x +1
END
SELECT #y = #y + 1
END
SELECT * FROM ##ALL_Dimentions;
EXECUTE SP_EXECUTESQL #FinalCode;
From executing the first query (by passing source DB and table name) you will get a pre-created execution query for the second SP, all you have to do is define is the column from your source:
+ Stable
+ Value (will be used to concentrate values based on that)
+ Dim (column you want to use to pivot by)
Names and datatypes will be defined automatically!
I cant recommend it for any production environments but does the job for adhoc BI requests.
I modified Taryn's answer ("Pivot with unknown number of columnnames" version) to show more than 1 row in the result. This requires to have an additional "Group" column
DROP TABLE #yourtable
CREATE table #yourtable
([Id] int,[Group] int, [Value] varchar(6), [ColumnName] varchar(13))
;
INSERT INTO #yourtable
([Id],[Group], [Value], [ColumnName])
VALUES
(1,1, 'John', 'FirstName'),
(2,1, '2.4', 'Amount'),
(3,1, 'ZH1E4A', 'PostalCode'),
(4,1, 'Fork', 'LastName'),
(5,1, '857685', 'AccountNumber'),
(6,2, 'Pedro', 'FirstName'),
(7,2, '5.1', 'Amount'),
(8,2, '123456', 'PostalCode'),
(9,2, 'Torres', 'LastName'),
(10,2, '857686', 'AccountNumber')
;
;
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols = STUFF((SELECT ',' + QUOTENAME(ColumnName)
from #yourtable
group by [Group], ColumnName, id
having [group] = (SELECT TOP 1 MIN([Group])FROM #yourtable)
order by id
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT ' + #cols + N' from
(
select value, ColumnName,[Group]
from #yourtable
GROUP BY [Group],ColumnName,Value
) x
pivot
(
max(value)
for ColumnName in (' + #cols + N')
) p '
exec sp_executesql #query;
Please try
CREATE TABLE pvt (Present int, [Absent] int);
GO
INSERT INTO pvt VALUES (10,40);
GO
--Unpivot the table.
SELECT Code, Value
FROM
(SELECT Present, Absent
FROM pvt) p
UNPIVOT
(Value FOR Code IN
(Present, [Absent])
)AS unpvt;
GO
DROP TABLE pvt
One more option which could be very useful is using CROSS APPLY
-- Original data
SELECT * FROM (VALUES ('1', 1, 2, 3),('2', 11, 22, 33)) AS Stage(id,col1,col2,col3)
-- row to columns using CROSS APPLY
SELECT Stage.id,v.idd, v.colc
FROM (VALUES ('1', 1, 2, 3),('2', 11, 22, 33)) AS Stage(id,col1,col2,col3)
CROSS APPLY (VALUES ('col1', col1),('col2', col2),('col3', col3)) AS v(idd,colc)
GO

Sqlserver table Row to column data - pivot table

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
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
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;
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
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.

How to pivot data in T-SQL for SQL Server

I have a table with the following structure where there are different types of results for a "SymbolQuoteID".
AnalysisResultID SymbolQuoteID ResultTypeID Result Updated
11 368 1 48.6750775191538 2015-10-08 22:09:15.680
12 368 2 47.7401046493826 2015-10-08 22:09:15.680
13 368 3 47.3978529584944 2015-10-08 22:09:15.680
14 368 4 43.4227483517635 2015-10-08 22:09:15.680
15 369 1 44.9316592945153 2015-10-08 22:09:15.680
16 369 2 48.8348167760945 2015-10-08 22:09:15.680
17 369 3 51.6463393199821 2015-10-08 22:09:15.680
18 369 4 51.7885923247485 2015-10-08 22:09:15.680
I need to SELECT the data so it is formatted like this where ResultTypeID is the column header and the corresponding Result column value is the column data. There is no aggregation, just the Result value. Also, the number of values for ResultTypeID will be consistent w/in a single query but may vary from query to query. For example, one time they might be 1 through 4 and another time 1 through 7, etc. Therefore, the number of columns in the pivoted table will change based upon the data.
SymbolQuoteID 1 2 3 4
368 48.6750775191538 47.7401046493826 47.3978529584944 43.4227483517635
369 44.9316592945153 48.8348167760945 51.6463393199821 51.7885923247485
It seems PIVOT might work but I've not used it before. Any help appreciated.
You can use dynamic PIVOT:
SQL Fiddle
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX);
SET #cols =
STUFF((
SELECT DISTINCT ',' + QUOTENAME(ResultTypeID)
FROM tbl
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
SET #query =
'SELECT SymbolQuoteID, ' + #cols + '
FROM (
SELECT
SymbolQuoteID, ResultTypeID, Result
FROM tbl
)t
PIVOT
(
MAX(Result)
FOR ResultTypeID IN (' + #cols + ')
) p '
PRINT (#query)
EXEC (#query)
You can also do this using dynamic crosstab:
SQL Fiddle
DECLARE #maxResultTypeId INT
SELECT TOP 1 #maxResultTypeId = ResultTypeId FROM tbl ORDER BY ResultTypeID DESC
DECLARE #sql NVARCHAR(MAX) = ''
SELECT #sql =
'SELECT
SymbolQuoteID' + CHAR(10)
SELECT #sql = #sql +
' , MAX(CASE WHEN ResultTypeId = ' + CONVERT(VARCHAR(3), rn) + ' THEN Result END) AS ' + QUOTENAME(rn) + CHAR(10)
FROM(
SELECT TOP(#maxResultTypeId)
ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rn
FROM sys.columns
)t
SELECT #sql = #sql +
'FROM tbl
GROUP BY SymbolQuoteID'
PRINT (#sql)
EXEC (#sql)
SELECT * FROM
(
SELECT SymbolQuoteID
,ResultTypeID
,Result
FROM TableName ) t
PIVOT (SUM(Result)
FOR ResultTypeID
IN ([1],[2],[3],[4])
)p

Resources