Grouping columns in pivot query - sql

Let's consider such query:
SELECT *
FROM (
SELECT
YEAR(OrderDate) [Year],
MONTH(OrderDate) [Month],
SubTotal
FROM Sales.SalesOrderHeader
) TableDate
PIVOT (
SUM(SubTotal)
FOR [Month] IN (
[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12]
)
) PivotTable
It returns a table looking like this:
year 1 2 3 4 5 6 ... 12 //MONTH
2001 100 100 100 100 100 100 100
How can we group the columns and return a result in the form,for example:
year 1-3 4-5 5-10 11-12 //Group the columns!
2001 300 200 500 200
I need a T-SQL query to do this. I 've checked dynamic query execution but it also doesn't help much..

In the subquery, first put your months in the right buckets, then perform a pivot on that table.
SELECT *
FROM (
SELECT
YEAR(OrderDate) [Year],
CASE WHEN MONTH(OrderDate)<=3 THEN '1-3'
WHEN MONTH(OrderDate)<=5 THEN '4-5',
WHEN MONTH(OrderDate)<=10 THEN '6-10',
ELSE '11-12' END [MonthRange]
SubTotal
FROM Sales.SalesOrderHeader
) TableDate
PIVOT (
SUM(SubTotal)
FOR [MonthRange] IN (
['1-3'],['4-5'],['6-10'],['11-12']
)
) PivotTable

I don't use PIVOT that much (and don't have enough info here to re-create your sample), but can't you just say instead of SELECT *:
SELECT [year], [1-3] = [1] + [2] + [3], [4-5] = [4] + [5], ...
FROM ( SELECT YEAR ...) TableDate
PIVOT ( SUM(SubTotal) FOR ...) PivotTable;
EDIT
Adding a simple working example for #t-clausen.dk:
CREATE TABLE #x([year] INT, [month] INT, total INT);
INSERT #x SELECT 2011, 1, 15
UNION ALL SELECT 2011, 2, 20
UNION ALL SELECT 2011, 2, 30
UNION ALL SELECT 2011, 3, 15
UNION ALL SELECT 2011, 4, 22
UNION ALL SELECT 2011, 5, 13
UNION ALL SELECT 2011, 6, 22
UNION ALL SELECT 2011, 7, 12
UNION ALL SELECT 2011, 8, 14
UNION ALL SELECT 2011, 9, 30
UNION ALL SELECT 2011, 10, 30
UNION ALL SELECT 2011, 11, 20
UNION ALL SELECT 2011, 12, 45;
SELECT
[year],
[1-3] = [1] + [2] + [3],
[4-5] = [4] + [5],
[6-10] = [6] + [7] + [8] + [9] + [10],
[11-12] = [11] + [12]
FROM ( SELECT [year], [month], total FROM #x ) AS TableDate
PIVOT (
SUM(total)
FOR [month] IN
(
[1], [2], [3], [4],
[5], [6], [7], [8],
[9],[10],[11],[12]
)
) AS PivotTable
ORDER BY [year];
DROP TABLE #x;

Related

Pivot table in SQL (column results to rows)

how do I pivot a results query?
currently it looks like this
Hours | Volume
8 | 1000
9 | 1012
10 | 1045
11 | 1598
12 | 1145
13 | 1147
into this
8 |9 |10 |11 |12 |13
1000|1012|1045|1598|1145|1147
I tried the following but its not working
Select #TEMP.volume,
pvt.volume,
#TEMP.volume
pvt.volume
FROM #TEMP
PIVOT (volume FOR [DialHour] IN ( "8", "9", "10", "11","12","13","14","15","16","17","18","19")) AS pvt
You can find a clear example on technet;
https://technet.microsoft.com/en-us/library/ms177410(v=sql.105).aspx
Your code should look more like the below;
Select volume, [8], [9], [10], [11],[12],[13],[14],[15],[16],[17],[18],[19]
FROM
(SELECT volume FROM #TEMP) AS SourceTable
PIVOT
(
hours
FOR volume IN ("8", "9", "10", "11","12","13","14","15","16","17","18","19")
) AS PivotTable;
I am creating this table in T-SQL for demonstration purposes.
CREATE TABLE #TEMP (HOURS INT, VOLUME INT)
INSERT INTO #TEMP VALUES
(8 ,1000),
(9 ,1012),
(10,1045),
(11,1598),
(12,1145),
(13,1147)
;
If you are not using T-SQL (SQL Server), you could run into issues with a Pivot example. The syntax is different in different platforms. Here is a more standard approach.
SELECT
MAX(CASE WHEN HOURS = 8 THEN VOLUME END) AS [8]
,MAX(CASE WHEN HOURS = 9 THEN VOLUME END) AS [9]
,MAX(CASE WHEN HOURS = 10 THEN VOLUME END) AS [10]
,MAX(CASE WHEN HOURS = 11 THEN VOLUME END) AS [11]
,MAX(CASE WHEN HOURS = 12 THEN VOLUME END) AS [12]
,MAX(CASE WHEN HOURS = 13 THEN VOLUME END) AS [13]
FROM #TEMP
;
It is better to label integers/numbers with an alphabet character instead of relying on brackets. This approach should work on any platform.
SELECT
MAX(CASE WHEN HOURS = 8 THEN VOLUME END) AS COL_8
,MAX(CASE WHEN HOURS = 9 THEN VOLUME END) AS COL_9
,MAX(CASE WHEN HOURS = 10 THEN VOLUME END) AS COL_10
,MAX(CASE WHEN HOURS = 11 THEN VOLUME END) AS COL_11
,MAX(CASE WHEN HOURS = 12 THEN VOLUME END) AS COL_12
,MAX(CASE WHEN HOURS = 13 THEN VOLUME END) AS COL_13
FROM #TEMP
;
This assumes you are working with SQL Server , then it is always better to use dynamic PIVOT approach
declare #col nvarchar(max), #query nvarchar(max)
select #col = stuff(
(select ','+quotename(Hours) from #tm for xml path('')),
1,1, '')
set #query = N'select * from
(
select * from #tm
)a
PIVOT
(
MAX(Volume) for Hours in ('+#col+')
)pvt'
EXECUTE sp_executesql #query
Result :
8 9 10 11 12 13
1000 1012 1045 1598 1145 1147
It is always good to use pivot query in addition to an ID column , to simplify u can get result set this way
WITH cte
AS (
SELECT *
FROM #TEMP
PIVOT(max(volume) FOR [DialHour] IN (
"8"
,"9"
,"10"
,"11"
,"12"
,"13"
,"14"
,"15"
,"16"
,"17"
,"18"
,"19"
)) AS pvt
)
SELECT 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19...
FROM cte

Count records by month & type

I have a table in SQL Server 2014 with this schema:
OccuredDate (date) TypeID (int)
2014-1-1 1
2014-1-2 1
2014-2-5 4
2015-5-23 2
2015-6-3 3
…it has thousands of rows comprised of dates & typeIDs, spanning years.
So that I can plot this to a charting component, I’m trying to build a query that for a given year 1) returns one row per-month that 2) counts the total number of TypeID instances for the given TypeIDs. The charting component prefers columns for the type counts.
So for "2014" it would look like this:
MonthDate TypeOne TypeTwo TypeThree TypeFour
2014-1-1 2 0 0 0
2014-2-1 0 0 0 1
or:
Year Month TypeOne TypeTwo TypeThree TypeFour
2014 Jan 2 0 0 0
2014 Feb 0 0 0 1
Spent most of the night on it but no luck. Is there some dark SQL magic that will do this?
thanks!
You can do it using pivot, with something like this:
SELECT OccuredDate, [1], [2], [3], [4]
FROM
(
SELECT OccuredDate, TypeID FROM Table1) AS SourceTable
PIVOT
(
count(TypeID) FOR TypeID IN ([1], [2], [3], [4])
) AS PivotTable
And per month version:
SELECT
DATEADD(month, DATEDIFF(month, 0, OccuredDate), 0) as Month,
sum([1]) as [1],
sum([2]) as [2],
sum([3]) as [3],
sum([4]) as [4]
FROM
(
SELECT OccuredDate, TypeID FROM Table1) AS SourceTable
PIVOT
(
count(TypeID) FOR TypeID IN ([1], [2], [3], [4])
) AS PivotTable
group by
DATEADD(month, DATEDIFF(month, 0, OccuredDate), 0)
You can test this in SQL Fiddle: daily and monthly
Edit: Rewrote the monthly SQL

SQL To Calculate "To Date" Values With Month Columns

SQL Afficianados,
There has got to be a better way than the road I am going down...
SQL Fiddle HERE
Using SQL Server 2008. In short, I have a table with months as columns. i.e.:
CREATE TABLE MyData (MyID VARCHAR(10),
JAN MONEY, FEB MONEY, MAR MONEY, APR MONEY, MAY MONEY, JUN MONEY,
JUL MONEY, AUG MONEY, SEP MONEY, OCT MONEY, NOV MONEY, DEC MONEY);
Given a month value of N (as integer, where 1 = JAN and 12 = DEC), I want to calculate a "To Date" value from the first month to the Nth month.
So, given this simple data:
INSERT INTO MyData (MyID, JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC)
SELECT 'Rec1', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
UNION ALL SELECT 'Rec2', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
UNION ALL SELECT 'Rec3', 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
;
I want to pass in any month value and add up the proper month columns. Given the sample data above, here is a chart of expected results based on the value of the month passed in...
Month Value | Expected Result |
------------+------------------
1 | 3
2 | 9
3 | 18
4 | 30
5 | 45
6 | 63
7 | 84
8 | 108
9 | 135
10 | 165
11 | 198
12 | 234
I know I could do this with a CASE statement like this:
DECLARE #v_Month INT = 2
SELECT CASE
WHEN #v_Month = 1 THEN SUM(JAN)
WHEN #v_Month = 2 THEN SUM(JAN) + SUM(FEB)
WHEN #v_Month = 2 THEN SUM(JAN) + SUM(FEB) + SUM(MAR)
--You get the idea. The pattern would continue for the rest of the months.
ELSE 0
END AS ToDateSum
FROM MyData
But is there a better way? Teach me, oh great ones of SQL Server.
You can use unpivot to change the data to a more amenable shape. It might make more sense to store it this way in the first place.
declare #v_Month int = 2;
with u as (
select
myid, [month], amount
from (
select
myid, jan [1], feb [2], mar [3], apr [4], may [5], jun [6],
jul [7], aug [8], sep [9], oct [10], nov [11], dec [12]
from
MyData
) p
unpivot (
amount for [month] in ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) u
) select
sum(amount)
from
u
where
[month] <= #v_Month;
Example SQLFiddle
You can simplify this a bit, if you want to keep the data in the same structure:
select
sum(amount)
from (
select
myid, jan [1], feb [2], mar [3], apr [4], may [5], jun [6],
jul [7], aug [8], sep [9], oct [10], nov [11], dec [12]
from
MyData
) p
unpivot (
amount for [month] in ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) u
where
[month] <= #v_Month;
Something like this will work:
declare #MyDesiredMonth int = 3
with normalized_view as
(
select MyId , Month = 1 , Value = Jan from MyData
UNION ALL select MyId , Month = 2 , Value = Feb from MyData
UNION ALL ...
UNION ALL select MyId , Month = 11 , Value = Nov from MyData
UNION ALL select MyId , Month = 12 , Value = Dec from MyData
)
select MonthlyTotal = sum(Value)
from normalized_view t
where t.Month = #myDesiredMonth
Alternatively, something like this:
declare #MyDesiredMonth int = 3
select MonthlyTotal = sum( case #MyDesiredMonth
when 1 then t.Jan
when 2 then t.Feb
...
when 11 then t.Nov
when 12 then t.Dec
end
)
from MyTable t
Either one should work with the execution plan likely winding up similar.

How to select data based Group by ID,Year,Month in sqlserver?

How to get the data based on the given below format:
Id name year month amount
1 A 2012 jan 100
1 A 2012 jan 900
1 A 2012 jan 300
1 A 2012 apr 100
1 A 2012 apr 500
2 B 2013 may 100
Output would be in the below mentioned form, if name, year, and month in parameter,
Id name Jan feb mar Apr may jun ...... jan .....may total
1 A 1300 0 0 600 0 0 ..... 0 ...... 0 1900
2 B 0 0 0 0 0 0.........0.......100 100
declare #t table (Id INT,name VARCHAR(10),years VARCHAR(10),months VARCHAR(10),amt INT )
insert into #t (Id,name,years,months,amt)values (1,'A','2012','jan',100)
insert into #t (Id,name,years,months,amt)values (2,'A','2012','jan',100)
insert into #t (Id,name,years,months,amt)values (3,'A','2012','apr',200)
insert into #t (Id,name,years,months,amt)values (4,'A','2012','apr',100)
insert into #t (Id,name,years,months,amt)values (5,'B','2013','may',200)
Select id,
name,
ISNULL(jan,0) As Jan,
ISNULL(feb,0) As FEb,
ISNULL(mar,0) As Mar,
ISNULL(apr,0)As Apr,ISNULL(JUn,0)As Jun,ISNULL(jul,0)As jul,ISNULL(aug,0)As aug
from
(Select distinct t.ID,t.name,t.years,t.months As Months,t.amt
from #t t)t
PIVOT (SUM(amt)FOR Months IN( [jan],
[feb],
[mar],
[apr],[JUn],[jul],[aug]))p
You need to use PIVOT to get result like you mentioned
Here is one example to use PIVOT
and your sql syntex like
SELECT * FROM (SELECT t.id,t.name,t.month,t.amount,(SELECT SUM(t2.amount) FROM dbo.test AS t2 GROUP BY t2.name HAVING t2.name= t.name ) AS total FROM dbo.test AS t) as s
PIVOT
(
SUM(Amount)
FOR [month] IN (jan, feb, mar, apr,
may, jun, jul, aug, sep, oct, nov, dec)
)AS pivots
Example
I think this might need a dynamic pivot?
CREATE TABLE #Data (
Id INT,
name VARCHAR(1),
[year] INT,
[month] VARCHAR(3),
amount INT);
INSERT INTO #Data VALUES (1, 'A', 2012, 'jan', 100);
INSERT INTO #Data VALUES (1, 'A', 2012, 'jan', 900);
INSERT INTO #Data VALUES (1, 'A', 2012, 'jan', 300);
INSERT INTO #Data VALUES (1, 'A', 2012, 'apr', 100);
INSERT INTO #Data VALUES (1, 'A', 2012, 'apr', 500);
INSERT INTO #Data VALUES (2, 'B', 2013, 'may', 100);
DECLARE #cols VARCHAR(1024);
SELECT
#Cols = STUFF((
SELECT DISTINCT
',' + QUOTENAME(CONVERT(VARCHAR(4), [year]) + '/' + [month])
FROM
#Data
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'),1 ,1 ,'');
DECLARE #Query VARCHAR(MAX);
SELECT #Query = '
WITH Aggregated AS (
SELECT
Id,
name,
CONVERT(VARCHAR(4), [year]) + ''/'' + [month] AS YearMonth,
SUM(amount) AS amount
FROM
#Data
GROUP BY
Id,
name,
[year],
[month])
SELECT
*
FROM
Aggregated
PIVOT (
SUM(amount)
FOR YearMonth IN (' + #cols + ')
) p;';
EXEC (#Query);
Results look like this:
Id name 2012/apr 2012/jan 2013/may
1 A 600 1300 NULL
2 B NULL NULL 100

How to convert rows to column in T-SQL, and write it in a temp table?

This is a question maybe already asked.
My query is
SELECT Year, Month, Line, SUM(value) as total FROM myTable
I've the following query result table:
Year Month Line Total
-------------------------------------------
2011 2 B1 5203510.00
2011 3 B1 2228850.00
2011 4 B1 7258075.00
2011 5 B1 6305370.00
2011 6 B1 5540180.00
2011 7 B1 7624430.00
2011 8 B1 4042300.00
2011 9 B1 3308870.00
2011 10 B1 4983875.00
2011 11 B1 4636500.00
2011 12 B1 3987350.00
2012 1 B1 518400.00
I would like the following:
Year Line Jan Feb Mar Apr ..... December
2011 B1 0 52035 2228 725 ..... 3987350
2012 B1 51840 ... ... ....
Please, can you help me how to translate query SQL from rows to columns?
Essentially, you need to PIVOT your data. There are several examples on SO on how to do this. The tricky part is to convert the month number to a month name.
This is accomplished in the example with DATENAME(month, DateAdd(month, [Month], 0)-1)
SQL Statement
SELECT *
FROM (
SELECT Year, Line, Total, mnt = DATENAME(month, DateAdd(month, [Month], 0)-1)
FROM myTable
) mt
PIVOT (MAX(Total) FOR [mnt] IN ([January],[February],[March],[April],[May],[June],[July],[August],[September],[October],[November],[December])) AS PVT
Test script
;WITH myTable AS (
SELECT * FROM (VALUES
(2011 , 2 , 'B1', 5203510.00)
, (2011 , 3 , 'B1', 2228850.00)
, (2011 , 4 , 'B1', 7258075.00)
, (2011 , 5 , 'B1', 6305370.00)
, (2011 , 6 , 'B1', 5540180.00)
, (2011 , 7 , 'B1', 7624430.00)
, (2011 , 8 , 'B1', 4042300.00)
, (2011 , 9 , 'B1', 3308870.00)
, (2011 , 10 , 'B1', 4983875.00)
, (2011 , 11 , 'B1', 4636500.00)
, (2011 , 12 , 'B1', 3987350.00)
, (2012 , 1 , 'B1', 518400.00)
) AS myTable (Year, Month, Line, Total)
)
SELECT *
FROM (
SELECT Year, Line, Total, mnt = DATENAME(month, DateAdd(month, [Month], 0)-1)
FROM myTable
) mt
PIVOT (MAX(Total) FOR [mnt] IN ([January],[February],[March],[April],[May],[June],[July],[August],[September],[October],[November],[December])) AS PVT
What you're trying to do is pivot the data. I will just use the Month and Total (relevant) columns.
If you're using MS SQL 2008 or up:
SELECT [1] AS Jan, [2] AS Feb, .. [12] AS Dec,
Total
FROM ( SELECT Month, Total FROM tableA ) AS SOURCE
PIVOT
( MAX(Total) AS Total
FOR
Month IN ([1],[2],...[12]) ) AS PIVOT
PIVOT is the T-SQL keyword you seek.