SQL Multi Pivot - sql

I am trying to do a pivot table to show order data by dayofyear. My first problem is my pivot doesn't appear to be showing the correct data. My second problem is I don't really want to type out a day for all 365 day columns. Is there an easier way?
Columns would be 1 - 365
Rows would be Year, #orders, #Tags
SELECT Yr, [01],[02],[03],[04],[05]....
FROM (
select TOP 100 PERCENT
YEAR(tagdata.shipdate) AS Yr,
DATEPART(dy,tagdata.shipdate) AS Day,
tagdata.#Orders,
tagdata.#Tags,
from tagData
GROUP BY tagData.ShipDate, tagdata.#Orders, tagdata.#Tags
) AS sourcetable
PIVOT
(
Max(#Orders) FOR Day IN ([01],[02],[03],[04],[05],.......),
Max(#Tags) FOR Day IN ([01],[02],[03],[04],[05],.......)
)
as pivottable
ORDER BY Yr

You have to do two pivots for each set of fields you are looking at and then join them on the year. Something like this:
SELECT isnull(pivottable1.Yr, pivottable2.Yr) Yr,
[1ORD],[2ORD],[3ORD],[4ORD],[5ORD],
[1TAG],[2TAG],[3TAG],[4TAG],[5TAG]
FROM (
select TOP 100 PERCENT
YEAR(shipdate) AS Yr,
Convert(varchar(3),DATEPART(dy,shipdate)) + 'ORD' AS Day,
#Orders
from #tagData
) AS sourcetable1
PIVOT
(
Max(#Orders) FOR Day IN ([1ORD],[2ORD],[3ORD],[4ORD],[5ORD])
) as pivottable1
full join (
select TOP 100 PERCENT
YEAR(shipdate) AS Yr,
Convert(varchar(3),DATEPART(dy,shipdate)) + 'TAG' AS Day,
#Tags
from #tagData
) AS sourcetable2
PIVOT
(
Max(#Tags) FOR Day IN ([1TAG],[2TAG],[3TAG],[4TAG],[5TAG])
) as pivottable2
on pivottable1.Yr = pivottable2.Yr
ORDER BY isnull(pivottable1.Yr, pivottable2.Yr)
As for all that typing... a simple script can cover you:
declare #number as int
declare #numbers as varchar(max)
set #number=1
while #number <= 365
begin
set #numbers = isnull(#numbers+',','') + '[' + convert(varchar(3),#number) + ']'
set #number=#number+1
end
print #numbers

Related

How to complete and fill in gaps between dates in SQL?

I have data in Redshift that I'm aggregating to the Year-Quarter level i.e. number of items by Year-Quarter
I need to show a continuous trend and hence I need to fill-in the gaps in Year-Quarter. The picture below should give a clearer idea of my current data and desired output.
How can I achieve this in Redshift SQL?
A query like this should do the trick:
create table test (yq int, items int);
INSERT INTO test Values (20201,10),(20204, 15),(20213, 25),(20222, 30);
with recursive quarters(q) as (
select min(yq) as q
from test
union all
select decode(right(q::text, 1), 4, q + 7, q + 1) as q
from quarters
where q < (select max(yq) from test)
)
select q as yq, decode(items is null, true,
lag(items ignore nulls) over (order by q), items) as items
from test t
right join quarters q
on t.yq = q.q
order by q;
It uses a recursive CTE to generate the quarters range needed, right joins this with the source data, and then uses a LAG() window function to populate the items if the value is NULL.
This is known as forward filling values:
CREATE TABLE #Temp
(
[YQ] nvarchar(5),
[items] int
)
INSERT INTO #Temp Values ('20201',10),('20204', 15),('20213', 25),('20222', 30)
---------------------------------------------------------------------------------
DECLARE #start int, #end int, #starty int, #endy int
SELECT #start=1, #end=4
SELECT #starty=MIN(Substring(YQ,0,5)), #endy=MIN(Substring(YQ,0,5)) from #Temp
;With cte1(y) as
(
Select #starty as y
union all
Select y + 1
from cte1
where y <= #endy + 1
)
, cte2(n) as
(
Select #start as n
union all
Select n + 1
from cte2
where n < #end
)
SELECT t1.YQ AS 'Year-Quarter',
CASE WHEN t2.items is null then (SELECT TOP 1 MAX(items) from #Temp WHERE items is not null and YQ < t1.YQ) ELSE t2.items END AS '# Items'
FROM
(
SELECT CAST(cte1.y AS nvarchar(4)) + CAST(cte2.n AS nvarchar(1)) AS YQ
FROM cte1, cte2
) t1
LEFT JOIN #Temp t2 ON t2.YQ = t1.YQ
WHERE t1.YQ <= (SELECT MAX(YQ) FROM #Temp)
ORDER BY t1.YQ, t2.items

SQL Server - Dynamic Pivot with 2 Group Variables and 2 Aggregate Calculations

I have a dataset that is shaped like this:
I am trying to convert the data to this format:
As you can see, I'd like to sum the accounts and revenue (for each month) by State and Account Type. It is important to note that I seek a dynamic solution as these ARE NOT the only values (hard-coding is not an option!).
What SQL query can I write to accomplish this task, dynamically? (as these values are not the only ones present in the complete dataset).
Thanks!
I'm assuming you want to keep the columns in order by date, thus the top 100 percent ... order by in the section where we generate the columns
Example
Declare #SQL varchar(max) = '
Select *
From (
Select [State]
,[AccountType]
,B.*
From YourTable A
Cross Apply ( values (concat(''Accounts_'',format([Date],''MM/dd/yyyy'')),Accounts)
,(concat(''Revenue_'' ,format([Date],''MM/dd/yyyy'')),Revenue)
) B (Item,Value)
) A
Pivot (sum([Value]) For [Item] in (' + Stuff((Select ','+QuoteName('Accounts_'+format([Date],'MM/dd/yyyy'))
+','+QuoteName('Revenue_' +format([Date],'MM/dd/yyyy'))
From (Select top 100 percent [Date] from YourTable Group By [Date] Order by [Date] ) A
For XML Path('')),1,1,'') + ') ) p'
--Print #SQL
Exec(#SQL)
Returns
If it helps, the generated SQL looks like this:
Select *
From (
Select [State]
,[AccountType]
,B.*
From YourTable A
Cross Apply ( values (concat('Accounts_',format([Date],'MM/dd/yyyy')),Accounts)
,(concat('Revenue_' ,format([Date],'MM/dd/yyyy')),Revenue)
) B (Item,Value)
) A
Pivot (sum([Value]) For [Item] in ([Accounts_12/31/2017],[Revenue_12/31/2017],[Accounts_01/31/2018],[Revenue_01/31/2018]) ) p

How to insert Select Query Result into a PIVOT

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

Get list of year ranges from list of years?

Is it possible to get a list of year ranges from a list of years?
Say there is a table like
Year
1990
1991
1992
1999
2001
2002
2015
Or like
Years
1990,1991,1992,1999,2001,2002,2015
I am not sure where to start; how can one get the ranges within those years? e.g.
1990-1992,1999,2001-2002,2015
Here is example:
SELECT *
INTO #years
FROM (VALUES
(1990),
(1991),
(1992),
(1999),
(2001),
(2002),
(2015)) AS D(Year)
SELECT STUFF((
SELECT ',' +
CASE
WHEN MIN(Year) = MAX(Year) THEN CAST(MIN(Year) AS VARCHAR(9))
WHEN MIN(Year) <> MAX(Year) THEN CAST(MIN(Year) AS VARCHAR(4)) + '-' +CAST(MAX(Year) AS VARCHAR(4))
END AS [text()]
FROM (
SELECT Year
,Year-ROW_NUMBER() OVER (ORDER BY Year) AS rowID
FROM #years) a
GROUP BY rowID
FOR XML PATH('')
),1,1,'');
The main idea is to find so called islands, which in this case is easy made by using ROW_NUMBER in this select:
SELECT Year ,Year-ROW_NUMBER() OVER (ORDER BY Year)
Years will be subtracted from row numbers, which will mark same "islands". Meaning, if every next year are increasing by one as row number does, we will get same result number:
YEAR RowNR RESULT
1999 1 1998
2000 2 1998
2015 3 2012
This result numbers can be later used for grouping and getting MAX and MIN values.
The technique to get actual ranges is known as islands and gaps. And to get values aggregated in one row you can use different techniques, but here's simple one with accumulating data in the variable will work fine.
declare #temp table (y int)
declare #res nvarchar(max)
insert into #temp (y)
values
(1990),
(1991),
(1992),
(1999),
(2001),
(2002),
(2015)
;with cte_rn as (
select
row_number() over(order by y) as rn, y
from #temp
), cte_rng as (
select
case
when count(*) = 1 then cast(min(y) as nvarchar(max))
else cast(min(y) as nvarchar(max)) + '-' + cast(max(y) as nvarchar(max))
end as rng
from cte_rn
group by y - rn
)
select #res = isnull(#res + ', ', '') + rng
from cte_rng
sql fiddle demo

SQL dynamic pivot after CTE

I hope this is more specific? sorry if I am unclear, kind of new to this. Thank you for the help!!
I'm trying to get a dynamic pivot to work on a CTE. I have looked around a bit and I have a couple of problems. For what I fount, it seems that something like the following post is pretty standard for a dynamic sql:
Pivot Table and Concatenate Columns
I have the following columns in my table with trades:
Date | product | time | price | volume |
I want to get the average price for each quarter of the day, so I want to pivot the time column after rounding it down to the nearest quarter time. and taking the Weighted average price per product and date.
so I use one CTE to create the pivot list:
DECLARE #pivot_list as varchar(max)
;with startquarter(starttradequarter)
AS
(
SELECT cast(DATEadd(mi,(datediff(mi,0,Time))/15*15,0)as varchar)
from [table]
where date > '2014-04-15'
),
PIVOT_CODES(PIVOT_CODE)
AS
(
SELECT DISTINCT starttradequarter AS PIVOT_CODE
from startquarter
)
SELECT #pivot_list = COALESCE(#pivot_list + ',[' + PIVOT_CODE + ']','[' + PIVOT_CODE + ']')
FROM PIVOT_CODES
then I want to use this variable in a pivot of the table:
;With productselector(Date,startquarter,product,volume,price)
as
(
SELECT [Date]
,cast(DATEadd(mi,(datediff(mi,0,Time))/15*15,0)as varchar) as startquarter
,[product]
,[Volume]
,[Price]
FROM [table]
where DelDate = '2014-01-06' and product = 'x'
),
WAPricequarter(startquarter,date,sumvolume,WAPq,product)
AS
(
SELECT startquarter
,Date
,sum(volume) as sumvolume
,round(sum(volume*price)/sum(volume),2) as WAPq
,product
from productselector
group by date, startquarter, product
)
SELECT date, product, + #pivot_list
from WAPricequarter
PIVOT (
SUM([sumvolume])
FOR startquarter IN (#pivot_list)
) AS pvt
So I see in all dynamic pivots the second statement first put in a variable and then executed, is this necessary?
If not how do I get the pivot to work on the columns in the #pivot_list, it now gives an incorrect syntax error that I can't get to work.
If it is necessary to put it in a variable and then execute, how can I then still filter for product or date inside that variable since I have to use '' around it.