How to insert Select Query Result into a PIVOT - sql

I m using SQL SERVER 2012.
Query:1
SELECT *
FROM (
SELECT Insurance, ChargeValue, CreatedDate
FROM dailychargesummary
WHERE MonthName='June 2017'
) m
PIVOT (
SUM(ChargeValue)
FOR CreatedDate IN ([06/22/2017], [06/23/2017],[06/30/2017])
) n
Output of above query is looks like below:
Now I m hard coding all the dates of a month inside the Pivot Query such as 06/01/2017, 06/02/2017, etc., After searching in the Google, I got the following query to display all the dates of a given month number.
Query 2:
DECLARE #month AS INT = 5
DECLARE #Year AS INT = 2016
;WITH N(N)AS
(SELECT 1 FROM(VALUES(1),(1),(1),(1),(1),(1))M(N)),
tally(N)AS(SELECT ROW_NUMBER()OVER(ORDER BY N.N)FROM N,N a)
SELECT datefromparts(#year,#month,N) date FROM tally
WHERE N <= day(EOMONTH(datefromparts(#year,#month,1)))
Output looks like below:
Can anyone please guide me how to use the Query2 inside the pivot in Query1 to automate the dates.

You need to use dynamic query, to get the pivot list dynamically
first assign the dates list to a variable.
Declare #pivot_list varchar(8000)= ''
DECLARE #month AS INT = 5
DECLARE #Year AS INT = 2016
;WITH N(N) AS (SELECT 1 FROM(VALUES(1),(1),(1),(1),(1),(1))M(N)),
tally(N) AS (SELECT ROW_NUMBER()OVER(ORDER BY N.N)FROM N,N a)
select #pivot_list = stuff((SELECT ','+quotename(convert(varchar(15),datefromparts(#year,#month,N),101))
FROM tally
WHERE N <= day(EOMONTH(datefromparts(#year,#month,1))) for xml path('')),1,1,'')
--Print #pivot_list
Now use the #pivot_list variable in the pivot list
Declare #sql varchar(8000)
set #sql = '
SELECT *
FROM (
SELECT Insurance, ChargeValue, CreatedDate
FROM dailychargesummary
WHERE MonthName='June 2017'
) m
PIVOT (
SUM(ChargeValue)
FOR CreatedDate IN ('+#pivot_list+')
) n'
--Print #sql
Exec #sql

Related

Divided by some number into column SQL

Well, I have a number which stored in a column. If I want to divide the value, I just need:
select columnName / 12 from myTable
Is it possible to put the result into 12 column? I want to make the 12 is flexible. So for instance, if I divide the value by 4, so the result should be 4 column.
Value Result1 Result2 Result3 Result4
12000 3000 3000 3000 3000
Does anyone know how to achieve this?
Thank you.
This achievable using dynamic query.
declare #cols nvarchar(max);
declare #sql nvarchar(1000);
with cte as (
select 12000 as col1, 12000/4 as col2
union all
select col1-col2, col2 from cte where col1 > col2
)
select #cols =
STUFF((select N',' + QUOTENAME(col2) from cte
FOR XML PATH('')
), 1, 1, '') + N'';
select #cols
You can avoid dynamic SQL if you know a maximum count:
Start with a mockup-table to simulate your issue
DECLARE #tbl TABLE(InitialValue DECIMAL(16,4));
INSERT INTO #tbl VALUES(12000),(20000),(10000);
--To test this I use a divisor of 4, try with other numbers
DECLARE #divisor DECIMAL(16,4)=4;
--This is the query
SELECT p.*
FROM
(
SELECT t.InitialValue / #divisor As DivResult
,t.InitialValue
,CONCAT('div',FORMAT(A.Nmbr,'00')) AS ColumnName
FROM #tbl t
CROSS APPLY(SELECT TOP(CAST(#divisor AS INT)) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values) A(Nmbr)
) t
PIVOT
(MAX(DivResult) FOR ColumnName IN(div01,div02,div03,div04,div05,div06,div07 /*add as many as you might need*/)) p;
The result
InitialValue div01 div02 div03 div04 div05 div06 div07
10000.0000 2500.000000000000000000000 2500.000000000000000000000 2500.000000000000000000000 2500.000000000000000000000 NULL NULL NULL
12000.0000 3000.000000000000000000000 3000.000000000000000000000 3000.000000000000000000000 3000.000000000000000000000 NULL NULL NULL
20000.0000 5000.000000000000000000000 5000.000000000000000000000 5000.000000000000000000000 5000.000000000000000000000 NULL NULL NULL
As you can see, the unused columns are returned but stay NULL.
I'd prefer this approach over dynamic sql as the consumer is better of in most cases if the result set and its structure is fixed and predictable...
Hint: You can add the divisor to your result set if needed...
It has to be dynamic query
Check below
Create TABLE Table1 (Origional int)
Declare #DivisonValue INT = 4
insert into Table1
VALUES (12000)
--Change the Top (12) to you what ever number you like. this will be your total number of columns
DECLARE #Columns VARCHAR(MAX) = (SELECT
',' + C.ColumnName + ' = Origional / ' + CAST(#DivisonValue AS VARCHAR(5))
/* Above line which you need to change for division */
FROM
(SELECT TOP (#DivisonValue)
ColumnId = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
,ColumnName ='Result' + CONVERT(VARCHAR(10), ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
) AS C
ORDER BY
C.ColumnId
FOR XML PATH (''))
DECLARE #FullQuery VARCHAR(MAX) = 'SELECT Origional,'+ substring(#Columns,2,LEN(#Columns)-1) + ' FROM Table1'
EXEC (#FullQuery)
DROP TABLE table1
GO
Change the logic as per your need for division.

how can I use Pivot using a variable in SQL?

I have paycode numbers and Names from table Paycodes
and I have Amount in monthlyTransaction. as follows:
Paycodes
Code Name
1 Basic Salary
2 Variable Deduction Amount
3 Fixed/Var Insurance PayCode
MonthlyTransaction
Code Amount
1 3000
2 10000
1 130000
1 150000
3 120000
I want it to be like this using pivot
Basic Salary Variable Deduction Amount Fixed/Var Insurance PayCode
31000 10000 120000
I want to use pivot to sum the Amount of each Paycode and I used this:-
DECLARE #data AS NVARCHAR(MAX)
DECLARE #query AS NVARCHAR(MAX)
-- DECLARE #data TABLE
--(
-- PaycodeName NVARCHAR(max)
--)
--INSERT INTO #data
-- ( PaycodeName )
--select dbo.Paycode.PayCodeName FROM dbo.Paycode
select #data = Paycode.PayCodeName FROM Paycode
set #query = 'SELECT * FROM (
SELECT Paycode.Name , MonthlyTransaction.Amount
From MonthlyTransaction
LEFT JOIN dbo.Paycode ON Paycode.code = MonthlyTransaction.Paycode
) AS s
PIVOT
(
SUM(Amount)
FOR Paycode.Name IN ('+#data+')
) AS pvt '
EXECUTE Sp_executesql #query
When I print #data it retrieve the last paycode only !
Can anyone help ?
To get all the paycodes in #data use
SELECT #data = #data + Paycode.PayCodeName + ', '
FROM Paycode
Then remove the last comma
the answer is :
DECLARE #codeNameCol NVARCHAR(max)
SELECT #codeNameCol= COALESCE(#codeNameCol + ',','') + QUOTENAME(RTRIM(LTRIM(PayCodeName)))
FROM (SELECT DISTINCT PayCodeName FROM Paycode) AS codeNameCol
DECLARE #querys NVARCHAR(max)
Set #querys='
SELECT *
FROM ( SELECT dbo.EmpAssignment.EmployeeId ,
PayCodeName ,
Amount
FROM dbo.MonthlyTransaction
LEFT JOIN dbo.Paycode ON Paycode.code = MonthlyTransaction.Paycode
LEFT JOIN dbo.EmpAssignment ON EmpAssignment.EmpId = MonthlyTransaction.EmpId
LEFT JOIN dbo.PayrollGroup ON PayrollGroup.PayrollGroup = EmpAssignment.PayrollGroup
) DataTable PIVOT
( SUM(Amount) FOR PayCodeName IN ( '+#codeNameCol+' ) ) PivotTable;'
EXEC (#querys)
This should do the trick
SELECT 1 Code, 'Basic Salary' Name
INTO #Paycode
UNION
SELECT 2 Code, 'Variable Deduction Amount' Name
UNION
SELECT 3 Code, 'Fixed/Var Insurance PayCode' Name
SELECT 1 Code, 3000 Amount
INTO #MonthTrans
UNION
SELECT 2 Code, 10000 Amount
UNION
SELECT 1 Code, 130000 Amount
UNION
SELECT 1 Code, 150000 Amount
UNION
SELECT 3 Code, 120000 Amount
DECLARE #data AS NVARCHAR(MAX)
SELECT #data = ISNULL(#data,'') + '[' +CAST(Code AS varchar) + '], '
FROM #Paycode
SELECT #data=SUBSTRING(#data, 0, LEN(#data))
DECLARE #query AS NVARCHAR(MAX)
SELECT #query = 'SELECT *
FROM
#MonthTrans M
PIVOT (
SUM(Amount) FOR Code IN (' + #data + ')
) pvt'
EXECUTE Sp_executesql #query
drop table #paycode
drop table #MonthTrans

How can I run my custom function and query in a loop for different time frames?

I am writing a function to calculate the total number of seconds a user was online at my website. Afterwards, I convert the number of seconds to hh:mm:ss:
select * into #temp from [MyFunction](timestamp1, timestamp2);
select u.Name,
convert(varchar(8), t.Seconds / 3600) + ':'
+ right('0', convert(varchar(2) t.Seconds % 3600/60), 2) + ':'
+ right('0', convert(varchar(2) t.Seconds % 60), 2)
as [Total Time]
from #temp t left join Users u
on t.UserID = u.UserID;
Where an example timestamp is 2016-04-01 00:00:00.000. What I want now, is to see total time spent on my website, not on 1 range, but a sequence of ranges, for instance:
2016-01-01 to 2016-01-15
2016-01-16 to 2016-01-31
2016-02-01 to 2016-02-15
Is it possible to put my code in a dynamic query to calculate all of these ranges by running the same code every time?
The output of my code above is:
Name [Total Time]
--------------------
Anton 6:34:55
Bert 5:22:14
What I would like is an output such as
Name [Period_1] [Period_2] [Period_3] [Period_4]
---------------------------------------------------
Anton 6:34:55 5:00:22 null 10:44:32
Bert 5:22:14 null null 9:22:53
So each range, or loop over the code, should be a column.
I believe pivot() will help me here, but any help kickstarting me with the dynamic SQL (or any better solution) would be greatly appreciated.
Wrap your current code into a procedure with parameters, something like:
CREATE PROCEUDRE dbo.CalcTime
#Period varchar(100) -- Name of the period
,#PeriodStart datetime -- Period starts
,#PeriodEnd datetime -- Period ends
and using appropriate datatypes.
Next, create a second procedure. Within this one, define another temporary table, like
CREATE TABLE #Results
(
Name varchar(100) not null -- Or however long it might get
,Period varchar(100) not null -- Ditto
,TotalTime int null -- *
)
Loop over every period you wish to define data for. For each period, call the "CalcTime" stored procedure, and dump the results into the temp table. Two ways to do this, use
INSERT #Results
execute dbo.CalcTime 'Period', 'Jan 1, 2016', 'Jan 15, 2016'
or, having defined the temp table in the calling procedure, you can reference it in the called procedure in a standard INSERT... SELECT... statement.
Also within the loop, build a comma-delimited string that lists all your period labels, e.g.
SET #AllPeriodLabels = isnull(#AllPeriodLabels + ',', '') + #ThisPeriodLabel
or,
SET #AllPeriodLabels = isnull(#AllPeriodLabels + ',', '') + '[' + #ThisPeriodLabel + ']' -- **
Use this to build the dynamic SQL pivot statement against the temp table, and you’re done. As mentioned in the comments, there are any number of SO posts on how to do that, and here are links to two: The first discusses building a dynamic pivot statement, and the second uses similar tactics for an unpivot statement.
* Avoid embedded spaces in object names, they will only give you pain.
** Ok, Sometimes you have to do it.
Two pseudo tables:
persons:
personId int
lastname nvarchar(50)
visits:
personid int
created datetime
duration int -- (store things like duration in seconds)
First make a list of the columns, here I used a created column and converted it to a month period. So the result is something like: [201501],[201502],[201503],....
declare #cols nvarchar(max)
set #cols = STUFF((select ',' + quotename(convert(VARCHAR(6), created, 112))
from visits
group by convert(VARCHAR(6), created, 112)
order by convert(VARCHAR(6), created, 112)
for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '')
I need dynamic SQL to fill in the variable number of COLs, I suggest you start with NON dynamic SQL, make it dynamic should be the last step.
declare #sql nvarchar(max)
set #sql = N'
select *
-- lazy create a temp so you don't have to bother about the column definitions
-- into #temp
from (
select p.lastname, convert(VARCHAR(6), created, 112) as period
-- this is optional to get a Grand Row total
-- ,(select sum(duration) from visits v where v.personId = p.personId) as total
from visits v
inner join persons p on v.personId = p.personId
) src
pivot (
sum(duration) for period in (' + #cols + ')
) pvt;
'
Well you can print this for verification or run it ...
exec sp_executesql #sql
You can make a twist by dumping the result in a temp table (created on the fly). That creates the opportunity to add extra columns for output, like an organization etc. etc..
alter table #temp add organization nvarchar(100)
Good luck !
Here is a working test code. Adapt it as you see fit.
Setup:
-- create test tables
CREATE TABLE Users
(
UserId INT,
UserName NVARCHAR(max)
)
CREATE TABLE Access
(
UserId INT,
StartTime DATETIME2,
EndTime DATETIME2
)
CREATE TABLE Periods
(
NAME NVARCHAR(max),
StartTime DATETIME2,
EndTime DATETIME2
)
go
-- function to format the time
CREATE FUNCTION ToTime(#SECONDS BIGINT)
returns NVARCHAR(max)
AS
BEGIN
RETURN CONVERT(VARCHAR(8), #SECONDS / 3600) + ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), #SECONDS % 3600/60), 2)
+ ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), #SECONDS % 60), 2)
END
go
-- populate values
INSERT INTO Users
VALUES (1, 'Anton'),
(2,'Bert')
DECLARE #I INT=100
DECLARE #D1 DATETIME2
DECLARE #D2 DATETIME2
WHILE ( #I > 0 )
BEGIN
SET #D1=Dateadd(second, Rand() * 8640000, Getdate())
SET #D2=Dateadd(second, Rand() * 1000, #D1)
INSERT INTO Access
VALUES (Floor(Rand() * 2 + 1), #D1, #D2);
SET #I=#I - 1
END
SET #I=1
SET #D1=Getdate()
WHILE ( #I < 6 )
BEGIN
SET #D2=Dateadd(day, 15, #D1)
INSERT INTO Periods
VALUES (Concat('Period_', #I),
#D1,
#D2);
SET #D1=#D2
SET #I=#I + 1
END
go
Working code:
-- Getting the values
DECLARE #COLS NVARCHAR(max)
SET #COLS = Stuff((SELECT ',' + Quotename(NAME)
FROM Periods
GROUP BY NAME
ORDER BY NAME
FOR xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, ''
)
DECLARE #SQL NVARCHAR(max)
SET #SQL = N'SELECT * FROM
(
SELECT u.UserName,
p.Name AS Period,
dbo.Totime(Sum(Datediff(SECOND,a.StartTime,a.EndTime))) AS [Time]
FROM Access a
INNER JOIN Users u
ON a.UserId=u.UserId
INNER JOIN Periods p
ON p.StartTime<=a.StartTime
AND a.StartTime<p.EndTime
GROUP BY u.UserName,
p.Name ) x PIVOT ( Max([Time]) FOR Period IN (' + #COLS +')
) p;'
--PRINT #SQL
EXECUTE(#SQL)

SQL Query - Rows to Columns but without pivot

I need an efficient way to convert the data returned in rows to columns for a football database I'm working on.
2 tables, one holding fixtures, one holding predictions.
Fixtures:
Predictions
I would like the data in those two table returned as follows (Can't post another link, but this query will give you an idea):
SELECT 1 as UserID,
2 as [Stoke vs Man United],
1 as [Bournemouth vs Crystal Palace],
2 as [Swansea vs West Brom],
1 as [Chelsea vs Watford],
3 as [Liverpool vs Leicester],
1 as [Tottenham vs Norwich],
2 as [Aston Villa vs West Ham]
The problem is, each week, the chosen teams will change so I couldn't get a pivot query to work. Any ideas?
2 things you need to know about to make this work.
Dynamic Pivot
sp_executesql
Example
-- Param Definitions
DECLARE #Sql NVARCHAR(MAX),
#Columns NVARCHAR(MAX),
#WeekNo INT = 6,
#UserID INT = 1,
#Params NVARCHAR(MAX) = N'#Week INT, #User INT'
-- Dynamic Column Names
SELECT #Columns = COALESCE(#Columns + ',','') + QUOTENAME(HomeTeamName + ' vs ' + AwayTeamName)
FROM usr_SS_Fixtures
WHERE WeekNo = #WeekNo
-- Dynamic SQL
SET #Sql = N'
SELECT *
FROM
(
SELECT
pred.UserId,
pred.ResultCode,
CONCAT(HomeTeamName,'' vs '', AwayTeamName) AS Teams
FROM
usr_SS_Fixtures fix
JOIN usr_SS_Predictions pred ON fix.EventID = pred.EventID AND fix.WeekNo = pred.WeekNo
WHERE
fix.WeekNo = #Week
AND pred.UserID = #User
) t
PIVOT
(
MAX(ResultCode)
FOR Teams IN (' + #Columns + ')
) p
'
-- Executes the dynamic SQL
EXECUTE sp_executesql #Sql, #Params, #Week = #WeekNo, #User = #UserID
DEMO
You have an interesting variation on the normal dynamic pivot problem, due to the known-in-advance maximum number of games, that makes one possible solution a little simpler. Try the SQL below; it creates a first row that contains the desired column headings, which may be sufficient for simple display purposes.
declare #weekNo as int = 6
,#UserID as int = 1;
with
fixture(UserID, EventID, [Value], GameNo) as (
select -1, EventID, AwayTeamName + ' at ' + HomeTeamName
, row_number() over (partition by WeekNo order by EventID)
from usr_SS_Fixtures
),
prediction(UserID, EventID, [Value], GameNo) as (
select UserID, EventID, ResultCode
, row_number() over (partition by WeekNo order by EventID)
from usr_SS_Predictions
),
data as (
select
WeekNo, UserID
,[1],[2],[3],[4],[5],[6],[7]
from (
select WeekNo, UserID, EventID, [Value] from fixture
union all
select WeekNo, UserID, EventID, [Value] from prediction
) T
pivot (
max([Value]) for GameNo in ([1],[2],[3],[4],[5],[6],[7])
) pvt
)
select
UserID,[1],[2],[3],[4],[5],[6],[7]
from data
where WeekNo = #WeekNo
order by
UserID

Dynamic SELECT statement, generate columns based on present and future values

Currently building a SELECT statement in SQL Server 2008 but would like to make this SELECT statement dynamic, so the columns can be defined based on values in a table. I heard about pivot table and cursors, but seems kind of hard to understand at my current level, here is the code;
DECLARE #date DATE = null
IF #date is null
set # date = GETDATE() as DATE
SELECT
Name,
value1,
value2,
value3,
value4
FROM ref_Table a
FULL OUTER JOIN (
SELECT
PK_ID ID,
sum(case when FK_ContainerType_ID = 1 then 1 else null) Box,
sum(case when FK_ContainerType_ID = 2 then 1 else null) Pallet,
sum(case when FK_ContainerType_ID = 3 then 1 else null) Bag,
sum(case when FK_ContainerType_ID = 4 then 1 else null) Drum
from
Packages
WHERE
#date between PackageStart AND PackageEnd
group by PK_ID ) b on a.Name = b.ID
where
Group = 0
The following works great for me , but PK_Type_ID and the name of the column(PackageNameX,..) are hard coded, I need to be dynamic and it can build itself based on present or futures values in the Package table.
Any help or guidance on the right direction would be greatly appreciated...,
As requested
ref_Table (PK_ID, Name)
1, John
2, Mary
3, Albert
4, Jane
Packages (PK_ID, FK_ref_Table_ID, FK_ContainerType_ID, PackageStartDate, PackageEndDate)
1 , 1, 4, 1JAN2014, 30JAN2014
2 , 2, 3, 1JAN2014, 30JAN2014
3 , 3, 2, 1JAN2014, 30JAN2014
4 , 4, 1, 1JAN2014, 30JAN2014
ContainerType (PK_ID, Type)
1, Box
2, Pallet
3, Bag
4, Drum
and the result should look like this;
Name Box Pallet Bag Drum
---------------------------------------
John 1
Mary 1
Albert 1
Jane 1
The following code like I said works great, the issue is the Container table is going to grow and I need to replicated the same report without hard coding the columns.
What you need to build is called a dynamic pivot. There are plenty of good references on Stack if you search out that term.
Here is a solution to your scenario:
IF OBJECT_ID('tempdb..##ref_Table') IS NOT NULL
DROP TABLE ##ref_Table
IF OBJECT_ID('tempdb..##Packages') IS NOT NULL
DROP TABLE ##Packages
IF OBJECT_ID('tempdb..##ContainerType') IS NOT NULL
DROP TABLE ##ContainerType
SET NOCOUNT ON
CREATE TABLE ##ref_Table (PK_ID INT, NAME NVARCHAR(50))
CREATE TABLE ##Packages (PK_ID INT, FK_ref_Table_ID INT, FK_ContainerType_ID INT, PackageStartDate DATE, PackageEndDate DATE)
CREATE TABLE ##ContainerType (PK_ID INT, [Type] NVARCHAR(50))
INSERT INTO ##ref_Table (PK_ID,NAME)
SELECT 1,'John' UNION
SELECT 2,'Mary' UNION
SELECT 3,'Albert' UNION
SELECT 4,'Jane'
INSERT INTO ##Packages (PK_ID, FK_ref_Table_ID, FK_ContainerType_ID, PackageStartDate, PackageEndDate)
SELECT 1,1,4,'2014-01-01','2014-01-30' UNION
SELECT 2,2,3,'2014-01-01','2014-01-30' UNION
SELECT 3,3,2,'2014-01-01','2014-01-30' UNION
SELECT 4,4,1,'2014-01-01','2014-01-30'
INSERT INTO ##ContainerType (PK_ID, [Type])
SELECT 1,'Box' UNION
SELECT 2,'Pallet' UNION
SELECT 3,'Bag' UNION
SELECT 4,'Drum'
DECLARE #DATE DATE, #PARAMDEF NVARCHAR(MAX), #COLS NVARCHAR(MAX), #SQL NVARCHAR(MAX)
SET #DATE = '2014-01-15'
SET #COLS = STUFF((SELECT DISTINCT ',' + QUOTENAME(T.[Type])
FROM ##ContainerType T
FOR XML PATH, TYPE).value('.', 'NVARCHAR(MAX)'),1,1,'')
SET #SQL = 'SELECT [Name], ' + #COLS + '
FROM (SELECT [Name], [Type], 1 AS Value
FROM ##ref_Table R
JOIN ##Packages P ON R.PK_ID = P.FK_ref_Table_ID
JOIN ##ContainerType T ON P.FK_ContainerType_ID = T.PK_ID
WHERE #DATE BETWEEN P.PackageStartDate AND P.PackageEndDate) X
PIVOT (COUNT(Value) FOR [Type] IN (' + #COLS + ')) P
'
PRINT #COLS
PRINT #SQL
SET #PARAMDEF = '#DATE DATE'
EXEC SP_EXECUTESQL #SQL, #PARAMDEF, #DATE=#DATE
Output:
Name Bag Box Drum Pallet
Albert 0 0 0 1
Jane 0 1 0 0
John 0 0 1 0
Mary 1 0 0 0
Static Query:
SELECT [Name],[Box],[Pallet],[Bag],[Drum] FROM
(
SELECT *
FROM
(
SELECT rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
) AS SourceTable
PIVOT
(
COUNT(PKID )
FOR [Type]
IN ( [Box],[Pallet],[Bag],[Drum])
) AS PivotTable
) AS Main
ORDER BY RFID
Dynamic Query:
DECLARE #columnList nvarchar (MAX)
DECLARE #pivotsql nvarchar (MAX)
SELECT #columnList = STUFF(
(
SELECT ',' + '[' + [Type] + ']'
FROM ContanerType
FOR XML PATH( '')
)
,1, 1,'' )
SET #pivotsql =
N'SELECT [Name],' + #columnList + ' FROM
(
SELECT *
FROM
(
SELECT rf.Name,cnt.[Type], pk.PK_ID AS PKID, rf.PK_ID AS RFID
FROM ref_Table rf INNER JOIN Packages pk ON rf.PK_ID = pk.FK_ref_Table_ID
INNER JOIN ContanerType cnt ON cnt.PK_ID = pk.FK_ContainerType_ID
) AS SourceTable
PIVOT
(
COUNT(PKID )
FOR [Type]
IN ( ' + #columnList + ')
) AS PivotTable
) AS Main
ORDER BY RFID;'
EXEC sp_executesql #pivotsql
Following my tutorial below will help you to understand the PIVOT functionality:
We write sql queries in order to get different result sets like full, partial, calculated, grouped, sorted etc from the database tables. However sometimes we have requirements that we have to rotate our tables. Sounds confusing?
Let's keep it simple and consider the following two screen grabs.
SQL Table:
Expected Results:
Wow, that's look like a lot of work! That is a combination of tricky sql, temporary tables, loops, aggregation......, blah blah blah
Don't worry let's keep it simple, stupid(KISS).
MS SQL Server 2005 and above has a function called PIVOT. It s very simple to use and powerful. With the help of this function we will be able to rotate sql tables and result sets.
Simple steps to make it happen:
Identify all the columns those will be part of the desired result set.
Find the column on which we will apply aggregation(sum,ave,max,min etc)
Identify the column which values will be the column header.
Specify the column values mentioned in step3 with comma separated and surrounded by square brackets.
So, if we now follow above four steps and extract information from the above sales table, it will be as below:
Year, Month, SalesAmount
SalesAmount
Month
[Jan],[Feb] ,[Mar] .... etc
We are nearly there if all the above steps made sense to you so far.
Now we have all the information we need. All we have to do now is to fill the below template with required information.
Template:
Our SQL query should look like below:
SELECT *
FROM
(
SELECT SalesYear, SalesMonth,Amount
FROM Sales
) AS SourceTable
PIVOT
(
SUM(Amount )
FOR SalesMonth
IN ( [Jan],[Feb] ,[Mar],
[Apr],[May],[Jun] ,[Jul],
[Aug],[Sep] ,[Oct],[Nov] ,[Dec])
) AS PivotTable;
In the above query we have hard coded the column names. Well it's not fun when you have to specify a number of columns.
However, there is a work arround as follows:
DECLARE #columnList nvarchar (MAX)
DECLARE #pivotsql nvarchar (MAX)
SELECT #columnList = STUFF(
(
SELECT ',' + '[' + SalesMonth + ']'
FROM Sales
GROUP BY SalesMonth
FOR XML PATH( '')
)
,1, 1,'' )
SET #pivotsql =
N'SELECT *
FROM
(
SELECT SalesYear, SalesMonth,Amount
FROM Sales
) AS SourceTable
PIVOT
(
SUM(Amount )
FOR SalesMonth
IN ( ' + #columnList +' )
) AS PivotTable;'
EXEC sp_executesql #pivotsql
Hopefully this tutorial will be a help to someone somewhere.
Enjoy coding.