pivot tables in sql server - sql

Have a table data as:
d1 d2 MON REPORT_DATE
67 46 Dec 2014-12-19 06:19:05.337
69 46 Dec 2014-12-22 06:21:47.430
67 85 Jan 2015-01-23 06:08:09.030
I need a result set as
DEC JAN
D1 69 67
D2 46 85
So far
SELECT *
FROM (SELECT PerValueStreamOnAutoDiags,
PerProdSupportOnAutoDiags,
LEFT(Datename(Month, ReportDate), 3)[Month],
ReportDate
FROM Temp_AutoApprovalMembercnt)AS s
PIVOT ( Max(ReportDate)
FOR [month] IN (dec,
jan) )AS p

This should work:
;with cte as
(select * from
(select PerValueStreamOnAutoDiags,
PerProdSupportOnAutoDiags,
LEFT(Datename(Month, ReportDate), 3)[Month]
from Temp_AutoApprovalMembercnt) as s
unpivot
(val
for cols in (PerValueStreamOnAutoDiags,PerValueStreamOnAutoDiags)) as u)
select * from
(select * from cte) as s
pivot
(max(val)
for [Month] in (DEC,JAN)) as p
Demo
First unpivot the data to bring PerValueStreamOnAutoDiags and PerProdSupportOnAutoDiags into rows from columns, and then pivot to get the data summarised by the month.

You need to pivot the data and reaggregate it, because you are swapping rows and columns. Here is a method using aggregation and union all:
select 'd1' as col, max(case when mon = 'Dec' then d1 end) as dec,
max(case when mon = 'Jan' then d1 end) as Jan
from Temp_AutoApprovalMembercnt
union all
select 'd2' as col, max(case when mon = 'Dec' then d2 end) as dec,
max(case when mon = 'Jan' then d2 end) as Jan
from Temp_AutoApprovalMembercnt;

Related

sql sum by month and year

I have tried by writing a correlated query but fails to result in output....I am trying using only joins now
SELECT sum(t1.acc)
,t1.mon
,t1.yr
FROM gl t1
WHERE mon IN (
SELECT mon
FROM gl t2
WHERE t2.mon <= t1.mon
AND t2.yr = t1.yr
)
GROUP BY t1.mon
,t1.yr
You should join your data with itself and apply the sum condition.
drop table if exists #input_data
select * into #input_data
from
(
select 1 as acc, 5 as mon, 2020 as yr union all
select 2 as acc, 6 as mon, 2020 as yr union all
select 3 as acc, 4 as mon, 2021 as yr union all
select 4 as acc, 3 as mon, 2021 as yr
) d
select
sum(e0.acc) as acc_sum,
d.mon,
d.yr
from
#input_data d
join #input_data e0
on e0.yr = d.yr and e0.mon <= d.mon
group by
d.yr, d.mon
order by
d.yr, d.mon
If you have multiple records for same mon/yr then you have to add additional grouping.
drop table if exists #input_data
select * into #input_data
from
(
select 1 as acc, 5 as mon, 2020 as yr union all
select 2 as acc, 6 as mon, 2020 as yr union all
select 3 as acc, 4 as mon, 2021 as yr union all
select 4 as acc, 3 as mon, 2021 as yr union all
select 0 as acc, 5 as mon, 2020 as yr
) d
;with cte_source as
(
select
sum(acc) as acc,
mon,
yr
from #input_data d
group by d.yr, d.mon
)
select
sum(e0.acc) as acc_sum,
d.mon,
d.yr
from
cte_source d
join cte_source e0
on e0.yr = d.yr and e0.mon <= d.mon
group by
d.yr, d.mon
order by
d.yr, d.mon
You seem to want a cumulative sum within a year:
select year, mon, sum(acc) over (partition by year order by mon)
from t
order by year, acc;
Getting the data back in the original order is a bit tricky but just requires an explicit order by.

Pivoting unique users for each month, each year

I'm learning about PIVOT function and I want to try it in my DB, in the table DDOT I have events (rows) made by users during X month Y year in the YYYYMM format.
id_ev iddate id_user ...
------------------------
1 201901 321
2 201902 654
3 201903 987
4 201901 321
5 201903 987
I'm basing my query on the MS Documentation and I'm not getting errors but I'm not able to fill it with the SUM of those unique events (users). In simple words I want to know how many users (unique) checked up each month (x axis) in the year (y axis). However, I'm getting NULL as result
YYYY jan feb mar
----------------------------
2019 NULL NULL NULL
I'm expecting a full table with what I mentionted before.
YYYY jan feb mar
----------------------------
2019 2 1 1
In the code I've tried with different aggregate functions but this block is the closest to a result from SQL.
CREATE TABLE ddot
(
id_ev int NOT NULL ,
iddate int NOT NULL ,
id_user int NOT NULL
);
INSERT INTO DDOT
(
[id_ev], [iddate], [id_user]
)
VALUES
(
1, 201901, 321
),
(
2, 201902, 654
),
(
3, 201903, 987
),
(
4, 201901, 321
),
(
5, 201903, 987
)
GO
SELECT *
FROM (
SELECT COUNT(DISTINCT id_user) [TOT],
DATENAME(YEAR, CAST(iddate+'01' AS DATETIME)) [YYYY], --concat iddate 01 to get full date
DATENAME(MONTH, CAST(iddate+'01' AS DATETIME)) [MMM]
FROM DDOT
GROUP BY DATENAME(YEAR, CAST(iddate+'01' AS DATETIME)),
DATENAME(MONTH, CAST(iddate+'01' AS DATETIME))
) AS DOT_COUNT
PIVOT(
SUM([TOT])
FOR MMM IN (jan, feb, mar)
) AS PVT
Ideally you should be using an actual date in the iddate column, and not a string (number?). We can workaround this using the string functions:
SELECT
CONVERT(varchar(4), LEFT(iddate, 4)) AS YYYY,
COUNT(CASE WHEN CONVERT(varchar(2), RIGHT(iddate, 2)) = '01' THEN 1 END) AS jan,
COUNT(CASE WHEN CONVERT(varchar(2), RIGHT(iddate, 2)) = '02' THEN 1 END) AS feb,
COUNT(CASE WHEN CONVERT(varchar(2), RIGHT(iddate, 2)) = '03' THEN 1 END) AS mar,
...
FROM DDOT
GROUP BY
CONVERT(varchar(4), LEFT(iddate, 4));
Note that if the iddate column already be text, then we can remove all the ugly calls to CONVERT above:
SELECT
LEFT(iddate, 4) AS YYYY,
COUNT(CASE WHEN RIGHT(iddate, 2) = '01' THEN 1 END) AS jan,
COUNT(CASE WHEN RIGHT(iddate, 2) = '02' THEN 1 END) AS feb,
COUNT(CASE WHEN RIGHT(iddate, 2) = '03' THEN 1 END) AS mar,
...
FROM DDOT
GROUP BY
LEFT(iddate, 4);

Pivot and get count of rows in each cell?

The following query gives me the year and month_num of each support ticket.
SELECT STRFTIME_UTC_USEC(created_at, '%Y') AS year,
STRFTIME_UTC_USEC(created_at, '%m') AS month_num
FROM zendesk.zendesk
I want to pivot the year values and show the COUNT(*) of all source rows in each cell, like this:
2014 2015 2016
01 5 ... ...
02 8
03 12
04 22
05 30
06 15
07 10
08 9
09 ...
10
11
12
How can I do this?
You can use conditional aggregation:
SELECT STRFTIME_UTC_USEC(created_at, '%m') AS month_num,
SUM(CASE WHEN STRFTIME_UTC_USEC(created_at, '%Y') = '2014' then 1 else 0 end) as cnt_2014,
SUM(CASE WHEN STRFTIME_UTC_USEC(created_at, '%Y') = '2015' then 1 else 0 end) as cnt_2015,
SUM(CASE WHEN STRFTIME_UTC_USEC(created_at, '%Y') = '2016' then 1 else 0 end) as cnt_2016
FROM zendesk.zendesk
GROUP BY month_num;
SELECT
month_num,
MIN(CASE WHEN [year] = '2014' THEN cnt END) AS year_2014,
MIN(CASE WHEN [year] = '2015' THEN cnt END) AS year_2015,
MIN(CASE WHEN [year] = '2016' THEN cnt END) AS year_2016
FROM (
SELECT
STRFTIME_UTC_USEC(created_at, '%Y') AS [year],
STRFTIME_UTC_USEC(created_at, '%m') AS month_num,
COUNT(*) AS cnt
FROM zendesk.zendesk
GROUP BY 1,2
)
GROUP BY 1

Add missing data from previous month or year cumulatively

Say I have the following data:
select 1 id, 'A' name, '2007' year, '04' month, 5 sales from dual union all
select 2 id, 'A' name, '2007' year, '05' month, 2 sales from dual union all
select 3 id, 'B' name, '2008' year, '12' month, 3 sales from dual union all
select 4 id, 'B' name, '2009' year, '12' month, 56 sales from dual union all
select 5 id, 'C' name, '2009' year, '08' month, 89 sales from dual union all
select 13 id,'B' name, '2016' year, '01' month, 10 sales from dual union all
select 14 id,'A' name, '2016' year, '02' month, 8 sales from dual union all
select 15 id,'D' name, '2016' year, '03' month, 12 sales from dual union all
select 16 id,'E' name, '2016' year, '04' month, 34 sales from dual
I want to cumulatively add up all the sales across all years and their respective periods (months). The output should look like the following:
name year month sale opening bal closing bal
A 2007 04 5 0 5
A 2007 05 2 5 7
B 2008 12 3 12 15
A 2008 04 0 5 5 -- to be generated
A 2008 05 0 7 7 -- to be generated
B 2009 12 56 15 71
C 2009 08 89 71 160
A 2009 04 0 5 5 -- to be generated
A 2009 05 0 7 7 -- to be generated
B 2016 01 10 278 288
B 2016 12 0 71 71 -- to be generated
A 2016 02 8 288 296
A 2016 04 0 5 5 -- to be generated
A 2016 05 0 7 7 -- to be generated
D 2016 03 12 296 308
E 2016 04 34 308 342
C 2016 08 0 160 160 -- to be generated
The Opening balance is the closing balance of previous month, and if it goes into next year than the opening balance for next year is the closing balance of the previous year. It should be able to work like this for subsequent years. I've got this part working. However, I don't know how to get around ths missing in say 2009 that exists in 2008. For instance the key A,2008,04 and also A,2008,05 does not exist in 2009 and the code should be able to add it in 2009 like above. Same applies for other years and months.
I'm working on Oracle 12c.
Thanks in advance.
A variation on #boneists approach, starting with your sample data in a CTE:
with t as (
select 1 id, 'A' name, '2007' year, '04' month, 5 sales from dual union all
select 2 id, 'A' name, '2007' year, '05' month, 2 sales from dual union all
select 3 id, 'B' name, '2008' year, '12' month, 3 sales from dual union all
select 4 id, 'B' name, '2009' year, '12' month, 56 sales from dual union all
select 5 id, 'C' name, '2009' year, '08' month, 89 sales from dual union all
select 13 id,'B' name, '2016' year, '01' month, 10 sales from dual union all
select 14 id,'A' name, '2016' year, '02' month, 8 sales from dual union all
select 15 id,'D' name, '2016' year, '03' month, 12 sales from dual union all
select 16 id,'E' name, '2016' year, '04' month, 34 sales from dual
),
y (year, rnk) as (
select year, dense_rank() over (order by year)
from (select distinct year from t)
),
r (name, year, month, sales, rnk) as (
select t.name, t.year, t.month, t.sales, y.rnk
from t
join y on y.year = t.year
union all
select r.name, y.year, r.month, 0, y.rnk
from y
join r on r.rnk = y.rnk - 1
where not exists (
select 1 from t where t.year = y.year and t.month = r.month and t.name = r.name
)
)
select name, year, month, sales,
nvl(sum(sales) over (partition by name order by year, month
rows between unbounded preceding and 1 preceding), 0) as opening_bal,
nvl(sum(sales) over (partition by name order by year, month
rows between unbounded preceding and current row), 0) as closing_bal
from r
order by year, month, name;
Which gets the same result too, though it also doesn't match the expected results in the question:
NAME YEAR MONTH SALES OPENING_BAL CLOSING_BAL
---- ---- ----- ---------- ----------- -----------
A 2007 04 5 0 5
A 2007 05 2 5 7
A 2008 04 0 7 7
A 2008 05 0 7 7
B 2008 12 3 0 3
A 2009 04 0 7 7
A 2009 05 0 7 7
C 2009 08 89 0 89
B 2009 12 56 3 59
B 2016 01 10 59 69
A 2016 02 8 7 15
D 2016 03 12 0 12
A 2016 04 0 15 15
E 2016 04 34 0 34
A 2016 05 0 15 15
C 2016 08 0 89 89
B 2016 12 0 69 69
The y CTE (feel free to use more meaningful names!) generates all the distinct years from your original data, and also adds a ranking, so 2007 is 1, 2008 is 2, 2009 is 3, and 2016 is 4.
The r recursive CTE combines your actual data with dummy rows with zero sales, based on the name/month data from previous years.
From what that recursive CTE produces you can do your analytic cumulative sum to add the opening/closing balances. This is using windowing clauses to decide which sales values to include - essentially the opening and closing balances are the sum of all values up to this point, but opening doesn't include the current row.
This is the closest I can get to your result, although I realise it's not an exact match. For example, your opening balances don't look correct (where did the opening balance of 12 come from for the output row for id = 3?). Anyway, hopefully the following will enable you to amend as appropriate:
with sample_data as (select 1 id, 'A' name, '2007' year, '04' month, 5 sales from dual union all
select 2 id, 'A' name, '2007' year, '05' month, 2 sales from dual union all
select 3 id, 'B' name, '2008' year, '12' month, 3 sales from dual union all
select 4 id, 'B' name, '2009' year, '12' month, 56 sales from dual union all
select 5 id, 'C' name, '2009' year, '08' month, 89 sales from dual union all
select 13 id, 'B' name, '2016' year, '01' month, 10 sales from dual union all
select 14 id, 'A' name, '2016' year, '02' month, 8 sales from dual union all
select 15 id, 'D' name, '2016' year, '03' month, 12 sales from dual union all
select 16 id, 'E' name, '2016' year, '04' month, 34 sales from dual),
dts as (select distinct year
from sample_data),
res as (select sd.name,
dts.year,
sd.month,
nvl(sd.sales, 0) sales,
min(sd.year) over (partition by sd.name, sd.month) min_year_per_name_month,
sum(nvl(sd.sales, 0)) over (partition by name order by to_date(dts.year||'-'||sd.month, 'yyyy-mm')) - nvl(sd.sales, 0) as opening,
sum(nvl(sd.sales, 0)) over (partition by name order by to_date(dts.year||'-'||sd.month, 'yyyy-mm')) as closing
from dts
left outer join sample_data sd partition by (sd.name, sd.month) on (sd.year = dts.year))
select name,
year,
month,
sales,
opening,
closing
from res
where (opening != 0 or closing != 0)
and year >= min_year_per_name_month
order by to_date(year||'-'||month, 'yyyy-mm'),
name;
NAME YEAR MONTH SALES OPENING CLOSING
---- ---- ----- ---------- ---------- ----------
A 2007 04 5 0 5
A 2007 05 2 5 7
A 2008 04 0 7 7
A 2008 05 0 7 7
B 2008 12 3 0 3
A 2009 04 0 7 7
A 2009 05 0 7 7
C 2009 08 89 0 89
B 2009 12 56 3 59
B 2016 01 10 59 69
A 2016 02 8 7 15
D 2016 03 12 0 12
A 2016 04 0 15 15
E 2016 04 34 0 34
A 2016 05 0 15 15
C 2016 08 0 89 89
B 2016 12 0 69 69
I've used Partition Outer Join to link any month and name combination in the table (in my query, the sample_data subquery - you wouldn't need that subquery, you'd just use your table instead!) to any year in the same table, and then working out the opening / closing balances. I then discard any rows that have an opening and closing balance of 0.

sql - max from group flag setter

I m trying to achieve flag setting for the condition in my table below
p_id mon_year e_id flag
---- --------- ----- -----
1 2011/11 20 0
1 2011/11 21 1
1 2012/01 22 1
1 2012/02 23 0
1 2012/02 24 0
1 2012/02 25 1
2 2011/11 28 0
2 2011/11 29 1
2 2012/01 30 1
grouping by p_id,e_id and mon_year, the flag is set for the last value in the month.
I m confused how can i achieve this
I tried to achieved this by using row_number and partition to seperate out the value. By still looking for to achieved
Output by using row_number query , i have got is as below:
Grouping by
p_id mon_year e_id row
---- --------- ----- -----
1 2011/11 20 1
1 2011/11 21 2
1 2012/01 22 1
1 2012/02 23 1
1 2012/02 24 2
1 2012/02 25 3
2 2011/11 28 1
2 2011/11 29 2
2 2012/01 30 1
Max of this value would set the flag column. But i m really bugged how to achieve it. Any help would be useful.
Thanks !!
I think this is what you're going for. . . The output exactly matches your example:
declare #t table (p_id int, [year] int, [month] int, [day] int)
insert #t select 1, 2011, 11, 20
union select 1, 2011, 11, 21
union select 1, 2012, 01, 22
union select 1, 2012, 02, 23
union select 1, 2012, 02, 24
union select 1, 2012, 02, 25
union select 2, 2011, 11, 28
union select 2, 2011, 11, 29
union select 2, 2012, 01, 30
select p_id, [year], [month], [day]
, case when r=1 then 1 else 0 end flag
from
(
select p_id, [year], [month], [day]
, row_number() over (partition by p_id, [year], [month] order by [day] desc) r
from #t
) x
order by p_id, [year], [month], [day]
Output:
p_id year month day flag
1 2011 11 20 0
1 2011 11 21 1
1 2012 1 22 1
1 2012 2 23 0
1 2012 2 24 0
1 2012 2 25 1
2 2011 11 28 0
2 2011 11 29 1
2 2012 1 30 1
Try ordering by descending. In that way, you don't have to look for maximum ROW_NUMBER but when ROW_NUMBER is 1 ;)
Something like this (I didn't completely understand what you want to achieve, so this is probably not 100% accurate):
WITH r_MyTable
AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY mon_year ORDER BY p_id, e_id DESC) AS GroupRank
FROM MyTable
)
UPDATE r_MyTable
SET flag = CASE WHEN GroupRank = 1 THEN 1 ELSE 0 END;
You can use max statement on e_id to get last value for the month, code is below:
IF OBJECT_ID('tempdb..#tmptest') IS NOT NULL
DROP TABLE #tmptest
SELECT
*
INTO
#tmptest
FROM
(
SELECT '1' p_id, '2011/11' mon_year, '20' e_id, '0' flag UNION ALL
SELECT '1', '2011/11', '21', '1' UNION ALL
SELECT '1', '2012/01', '22', '1' UNION ALL
SELECT '1', '2012/02', '23', '0' UNION ALL
SELECT '1', '2012/02', '24', '0' UNION ALL
SELECT '1', '2012/02', '25', '1' UNION ALL
SELECT '2', '2011/11', '28', '0' UNION ALL
SELECT '2', '2011/11', '29', '1' UNION ALL
SELECT '2', '2012/01', '30', '1'
) as tmp
SELECT
tmptest.*
FROM
(
SELECT
MAX(e_id) e_id
,p_id
,mon_year
FROM
#tmptest
GROUP BY
p_id,mon_year
) tblLastValueEID
INNER JOIN
#tmptest tmptest
ON
tmptest.p_id = tblLastValueEID.p_id
AND
tmptest.mon_year = tblLastValueEID.mon_year
AND
tmptest.e_id = tblLastValueEID.e_id