BigQuery pivot on more fields - google-bigquery

Following up on Pivot table data using GROUP BY ROLLUP, I would like to produce a pivot along the lines that Mikhail showed on a 3x3 pivot table. In Excel, it gives us 180 value cells:
It gives 128 non-empty cells, but 180 cells including empty cells (lower-right is the Count: 128, upper left is the 15Rx12C=180). Here is the query I have now which gives me 128 'value cells':
select
(case when grp_set & 1 > 0 then Reseller end) as Reseller,
(case when grp_set & 2 > 0 then ProductGroup end) as ProductGroup,
(case when grp_set & 4 > 0 then Product end) as Product,
(case when grp_set & 8 > 0 then Year end) as Year,
(case when grp_set & 16 > 0 then Quarter end) as Quarter,
(case when grp_set & 32 > 0 then Product_Info end) as Product_Info,
sum(Revenue) as Revenue,
sum(Units) as Units
from `first-outlet-750.biengine_tutorial.Product`, unnest(generate_array(1, 64)) grp_set
where Year IN (2020) and Quarter in ('Q1', 'Q2')
group by 1, 2, 3, 4, 5, 6
having not (Quarter is null and Product_Info is not null)
and not (Year is null and Quarter is not null)
and not (Reseller is null and ProductGroup is not null)
and not (ProductGroup is null and Product is not null)
order by 1, 2, 3, 4 , 5, 6
How would I adjust this query so it returns 180 cell-results (that is, 90 rows) instead of 128 (64 rows)?

Try below
select
(case when grp_set & 1 > 0 then Reseller end) as Reseller,
(case when grp_set & 2 > 0 then ProductGroup end) as ProductGroup,
(case when grp_set & 4 > 0 then Product end) as Product,
(case when grp_set & 8 > 0 then Year end) as Year,
(case when grp_set & 16 > 0 then Quarter end) as Quarter,
(case when grp_set & 32 > 0 then Product_Info end) as Product_Info,
sum(Revenue) as Revenue,
sum(Units) as Units
from `first-outlet-750.biengine_tutorial.Product`, unnest(generate_array(1, 64)) grp_set
where Year IN (2020) and Quarter in ('Q1', 'Q2')
group by 1, 2, 3, 4, 5, 6
having not (Quarter is null and Product_Info is not null)
and not (Year is null and Quarter is not null)
and not (ProductGroup is null and Product is not null)
order by 1, 2, 3, 4 , 5, 6

Related

Sum of particular column with month and year with fiscal year from custom date

I have following data in my table:
uniqueId d_date amount
1 2018-02-01 100.25
2 2019-03-01 456.5
3 2018-02-01 455
4 2019-05-01 200.48
5 2018-06-01 100
6 2019-07-01 200
7 2018-12-01 6950
8 2019-02-01 60
9 2020-01-20 100
Now when I enter start date = '2018-03-12' then my fiscal year must start with march 2018 to feb 2019 and so on.
If i enter start date = '2019-05-12' then my fiscal year must start with May 2019 to April 2020
I have tried below query but it is not working properly and also it calculate past year which is 2017 I do not want any data from past year from my entered custom date. So if entered start date = '2018-03-12' then is must start calculation for 2018 and above years only. No past year.
Declare #startdate as date
Declare #monthDate as int
Declare #ownmonth as int
set #startdate = '2018-03-12'
set #monthDate = month(#startdate)
set #ownmonth = 1
select
year(dateadd(month, -#monthDate, d_date)) year,
sum(case when month(d_date) = case when #monthDate+1 > 12 then #ownmonth else #monthDate+1 End then amount end) ,
sum(case when month(d_date) = case when #monthDate+2 > 12 then #ownmonth+1 else #monthDate+2 End then amount end) ,
sum(case when month(d_date) = case when #monthDate+3 > 12 then #ownmonth+2 else #monthDate+3 End then amount end) ,
sum(case when month(d_date) = case when #monthDate+4 > 12 then #ownmonth+3 else #monthDate+4 End then amount end) ,
sum(case when month(d_date) = case when #monthDate+5 > 12 then #ownmonth+4 else #monthDate+5 End then amount end) ,
sum(case when month(d_date) = case when #monthDate+6 > 12 then #ownmonth+5 else #monthDate+6 End then amount end) ,
sum(case when month(d_date) = case when #monthDate+7 > 12 then #ownmonth+6 else #monthDate+7 End then amount end) ,
sum(case when month(d_date) = case when #monthDate+8 > 12 then #ownmonth+7 else #monthDate+8 End then amount end) ,
sum(case when month(d_date) = case when #monthDate+9 > 12 then #ownmonth+8 else #monthDate+9 End then amount end) ,
sum(case when month(d_date) = case when #monthDate+10 > 12 then #ownmonth+9 else #monthDate+10 End then amount end) ,
sum(case when month(d_date) = case when #monthDate+11 > 12 then #ownmonth+10 else #monthDate+11 End then amount end) ,
sum(case when month(d_date) = case when #monthDate+12 > 12 then #ownmonth+11 else #monthDate+12 End then amount end) ,
sum(amount) total
from mytable
group by year(dateadd(month, -#monthDate, amount))
order by year
But above query does not show proper year & month wise data
Now I want output with fiscal year calculation:
Year Mar Apr May Jun July Aug Sept Oct Nov Dec Jan Feb Total
2018 - - - 100 - - - - - 6950 - 60 7110
2019 456.5 - 200.48 - 200 - - - - - 100 - 956.98
I can not use PIVOT as it is not supported in my compact SQL Server version.
How can I do this?
Your rule for a fiscal year is the year's March until the following year's February:
date | fiscal year
... | ...
2018-02-28 | 2017
2018-03-01 | 2018
... | 2018
2019-02-28 | 2018
2019-03-01 | 2019
... | ...
That means when we subtract two months from a date, we get a date the year of which is the fiscal year:
date | date - 2 months | fiscal year
... | ... | ...
2018-02-28 | 2017-12-28 | 2017
2018-03-01 | 2018-01-01 | 2018
... | ... | 2018
2019-02-28 | 2018-12-28 | 2018
2019-03-01 | 2019-01-01 | 2019
... | ... | ...
select
year(dateadd(month, -2, d_date)) as fiscal_year,
sum(case when month(d_date) = 3 then amount else 0 end) as mar,
sum(case when month(d_date) = 4 then amount else 0 end) as apr,
sum(case when month(d_date) = 5 then amount else 0 end) as may,
sum(case when month(d_date) = 6 then amount else 0 end) as jun,
sum(case when month(d_date) = 7 then amount else 0 end) as jul,
sum(case when month(d_date) = 8 then amount else 0 end) as aug,
sum(case when month(d_date) = 9 then amount else 0 end) as sep,
sum(case when month(d_date) = 10 then amount else 0 end) as oct,
sum(case when month(d_date) = 11 then amount else 0 end) as nov,
sum(case when month(d_date) = 12 then amount else 0 end) as dec,
sum(case when month(d_date) = 1 then amount else 0 end) as jan,
sum(case when month(d_date) = 2 then amount else 0 end) as feb,
sum(amount) as total
from mytable
group by year(dateadd(month, -2, d_date))
order by year(dateadd(month, -2, d_date));
If you want to limit this to the fiscal year a given date resides in, add:
where year(dateadd(month, -2, d_date)) = year(dateadd(month, -2, #given_date))
And well, if you want to limit this to the fiscal years beginning with that year, that would of course be:
where year(dateadd(month, -2, d_date)) >= year(dateadd(month, -2, #given_date))
UPDATE: You want a fiscal year to start with the first day of the month of a given date. I.e. If the given date is 1990-04-23, then a fiscal year starts with April. This changes above query only slightly, because rather than subtracting 2 months (for March), we must generalize this to subtracting one month less than the given month.
I am using a modulo operation when comparing months in order not to end up with months 13, 14, etc.
select
year(dateadd(month, - month(#startdate) + 1, d_date)) as fiscal_year,
sum(case when month(d_date) = (month(#startdate) + 0) % 12 then amount else 0 end) as first,
sum(case when month(d_date) = (month(#startdate) + 1) % 12 then amount else 0 end) as second,
sum(case when month(d_date) = (month(#startdate) + 2) % 12 then amount else 0 end) as third,
sum(case when month(d_date) = (month(#startdate) + 3) % 12 then amount else 0 end) as fourth,
sum(case when month(d_date) = (month(#startdate) + 4) % 12 then amount else 0 end) as fith,
sum(case when month(d_date) = (month(#startdate) + 5) % 12 then amount else 0 end) as sixth,
sum(case when month(d_date) = (month(#startdate) + 6) % 12 then amount else 0 end) as seventh,
sum(case when month(d_date) = (month(#startdate) + 7) % 12 then amount else 0 end) as eighth,
sum(case when month(d_date) = (month(#startdate) + 8) % 12 then amount else 0 end) as nineth,
sum(case when month(d_date) = (month(#startdate) + 9) % 12 then amount else 0 end) as tenth,
sum(case when month(d_date) = (month(#startdate) + 10) % 12 then amount else 0 end) as eleventh,
sum(case when month(d_date) = (month(#startdate) + 11) % 12 then amount else 0 end) as twelfth,
sum(amount) as total
from mytable
group by year(dateadd(month, - month(#startdate) + 1, d_date))
order by year(dateadd(month, - month(#startdate) + 1, d_date));
And again, if we want our results start from the fiscal year of the given date, we'd add:
where year(dateadd(month, - month(#startdate) + 1, d_date)) >= year(#startdate)

Quantity of data per month

I have to calculate the number of cars a day - of every month
I tried to do this using the Datediff formula
But I can't add the segmentation of each month either.
Attached script table:
create table TABLE_A(Code FLOAT,DateIn datetime,dateOut datetime,Garage varchar(30)
)
insert into Table_A (Code,DateIn,dateOut,Garage) values
('1','2018-06-07 00:00:00.000','2018-12-19 00:00:00.000','X'),
('2','2018-05-30 00:00:00.000','2018-12-19 00:00:00.000','Y'),
('3','2018-08-08 00:00:00.000','2018-11-18 00:00:00.000','Z'),
('4','2018-12-30 00:00:00.000','2018-12-30 00:00:00.000','Y'),
('5','2018-09-16 00:00:00.000','2018-10-19 00:00:00.000','Y'),
('6','2018-05-08 00:00:00.000','2018-08-28 00:00:00.000','Z'),
('7','2018-01-29 00:00:00.000','2018-07-31 00:00:00.000','Z'),
('8','2018-05-24 00:00:00.000','2018-09-10 00:00:00.000','X'),
('9','2018-05-02 00:00:00.000','2018-06-30 00:00:00.000','Y'),
('10','2018-07-05 00:00:00.000','2018-12-09 00:00:00.00','Z')
And this is the structure of the query result that should be:(Columns:Year,month,Garage-Number of vehicles per day by month)
Year month X Y Z
2018 1
2018 2
2018 3
2018 4
2018 5
2018 6
2018 7
2018 8
2018 9
2018 10
2018 11
2018 12
Thanks for the help.
You can first generate the list of months and year and then can left join your table to that -
WITH MONTHS AS (SELECT 1 MNTHS
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL
SELECT 9
UNION ALL
SELECT 10
UNION ALL
SELECT 11
UNION ALL
SELECT 12),
YEAR AS (SELECT 2018 YEAR)
SELECT YEAR,
MNTHS,
SUM(CASE WHEN Garage = 'X' THEN 1 ELSE 0 END) AS X,
SUM(CASE WHEN Garage = 'Y' THEN 1 ELSE 0 END) AS Y,
SUM(CASE WHEN Garage = 'Z' THEN 1 ELSE 0 END) AS Z
FROM (SELECT * FROM MONTHS M CROSS JOIN YEAR Y) YEARS
LEFT JOIN TABLE_A T ON YEARS.MNTHS = MONTH(T.DateIn)
AND YEARS.YEAR = YEAR(T.DateIn)
GROUP BY YEAR(DateIn),
MONTH(DateIn),
MNTHS,
YEAR
ORDER BY YEAR,
MNTHS
Here is the fiddle
The following query should do what you want, the Recursive CTE part is used to figure out dates between each DayIn and DayOut. Once we have the complete list of dates, in the main query we do a conditional aggregation to find out the DISTINCT number of cars in the garage each month,
CREATE TABLE TABLE_A (Code FLOAT,DateIn DATETIME,dateOut DATETIME,Garage VARCHAR(30))
INSERT INTO Table_A (Code,DateIn,dateOut,Garage) VALUES
('1','2018-06-07 00:00:00.000','2018-12-19 00:00:00.000','X'),
('2','2018-05-30 00:00:00.000','2018-12-19 00:00:00.000','Y'),
('3','2018-08-08 00:00:00.000','2018-11-18 00:00:00.000','Z'),
('4','2018-12-30 00:00:00.000','2018-12-30 00:00:00.000','Y'),
('5','2018-09-16 00:00:00.000','2018-10-19 00:00:00.000','Y'),
('6','2018-05-08 00:00:00.000','2018-08-28 00:00:00.000','Z'),
('7','2018-01-29 00:00:00.000','2018-07-31 00:00:00.000','Z'),
('8','2018-05-24 00:00:00.000','2018-09-10 00:00:00.000','X'),
('9','2018-05-02 00:00:00.000','2018-06-30 00:00:00.000','Y'),
('10','2018-07-05 00:00:00.000','2018-12-09 00:00:00.00','Z')
/** Main Query Starts Here **/
;WITH CTE ([Code],[DateIn],[DateOut],[Garage]) AS (
SELECT [Code], [DateIn], [DateOut], [Garage]
FROM TABLE_A WHERE [DateIn] <= [DateOut]
UNION ALL
SELECT [Code], DATEADD(DAY, 1, [DateIn]), [DateOut], [Garage]
FROM CTE
WHERE [DateIn] < [DateOut])
SELECT
YEAR([DateIn]) AS [Year]
,MONTH([DateIn]) AS [Month]
,COUNT( DISTINCT CASE WHEN [Garage] = 'X' THEN T.t1 ELSE NULL END) AS X
,COUNT( DISTINCT CASE WHEN [Garage] = 'Y' THEN T.t1 ELSE NULL END) AS Y
,COUNT( DISTINCT CASE WHEN [Garage] = 'Z' THEN T.t1 ELSE NULL END) AS Z
FROM CTE
CROSS APPLY (VALUES (CONVERT(VARCHAR(4),YEAR([DateIn])) + CONVERT(VARCHAR(2),MONTH([DateIn])) + CONVERT(VARCHAR(20),[Code]))) AS T(t1)
GROUP BY YEAR([DateIn]), MONTH([DateIn])
ORDER BY [Year], [Month]
OPTION (MAXRECURSION 0)
The result is as below,
Year Month X Y Z
2018 1 0 0 1
2018 2 0 0 1
2018 3 0 0 1
2018 4 0 0 1
2018 5 1 2 2
2018 6 2 2 2
2018 7 2 1 3
2018 8 2 1 3
2018 9 2 2 2
2018 10 1 2 2
2018 11 1 1 2
2018 12 1 2 1
You can generate the dates using a recursive subquery.
Then you have multiple ways to calculate the summary data. One simple method uses apply:
with dates as (
select convert(date, '2018-01-01') as dte, 1 as lev
union all
select dateadd(month, 1, dte), lev + 1
from dates
where lev < 12
)
select year(d.dte), month(d.dte), s.*
from dates d outer apply
(select sum(case when a.Garage = 'X' then 1 else 0 end) as x,
sum(case when a.Garage = 'Y' then 1 else 0 end) as y,
sum(case when a.Garage = 'Z' then 1 else 0 end) as z
from table_a a
where a.datein <= d.dte and a.dateout >= d.dte
) s;
Your question is a little vague on the exact calculation. This calculates the number of cars in each garage on the first of each month.
Here is a db<>fiddle.
EDIT:
Your revised question is more complicated, but since I started answering:
with dates as (
select convert(date, '2018-01-01') as dte, 1 as lev
union all
select dateadd(month, 1, dte), lev + 1
from dates
where lev < 12
)
select year(d.dte), month(d.dte),
sum(case when a.Garage = 'X'
then datediff(day,
(case when a.datein < d.dte then d.dte else datein end),
(case when a.dateout >= dateadd(month, 1, d.dte) then eomonth(d.dte) else dateout end)
) + 1
else 0
end) as x_cardays,
sum(case when a.Garage = 'Y'
then datediff(day,
(case when a.datein < d.dte then d.dte else datein end),
(case when a.dateout >= dateadd(month, 1, d.dte) then eomonth(d.dte) else dateout end)
) + 1
else 0
end) as y_cardays,
sum(case when a.Garage = 'Z'
then datediff(day,
(case when a.datein < d.dte then d.dte else datein end),
(case when a.dateout >= dateadd(month, 1, d.dte) then eomonth(d.dte) else dateout end)
) + 1
else 0
end) as z_cardays
from (select d.*, day(eomonth(dte)) as days_in_month
from dates d
) d left join
table_a a
on a.datein < dateadd(month, 1, d.dte) and a.dateout >= d.dte
group by d.dte
order by d.dte;
Overlaps with dates are a bit tricky, but you definitely want to do this with dates.
Note this doesn't calculate the average per day. You can divide by d.days_in_month if you want the average.
Here is a revised db<>fiddle.
This below script will only work if all your DateIn and DateOut for a particular record is in a single year like 2018.
Leap year Year is not considered but can be implemented.
WITH Month_Wise_Day AS
(
-- Listing/selecting 12 month manually here
-- With number of day for that month
SELECT 1 M, 31 ND UNION ALL
SELECT 2 M, 28 UNION ALL SELECT 3 M,31 UNION ALL SELECT 4 M,30 UNION ALL SELECT 5 M,31 UNION ALL
SELECT 6 M,30 UNION ALL SELECT 7 M,31 UNION ALL SELECT 8 M,31 UNION ALL SELECT 9 M,30 UNION ALL
SELECT 10 M,31 UNION ALL SELECT 11 M,30 UNION ALL SELECT 12 M,31
)
SELECT A.Year, A.Month,
SUM(CASE WHEN A.Garage = 'X' THEN A.No_of_days ELSE 0 END) AS X,
SUM(CASE WHEN A.Garage = 'Y' THEN A.No_of_days ELSE 0 END) AS Y,
SUM(CASE WHEN A.Garage = 'Z' THEN A.No_of_days ELSE 0 END) AS Z
FROM
(
SELECT A.*,
B.M AS Month,
YEAR(A.DateIn) AS Year,
CASE
WHEN MONTH(A.DateIn) = MONTH(A.DateOut) THEN DATEDIFF(DD,DateIn,DateOut) +1
WHEN B.M = MONTH(DateIn) THEN B.ND - DAY(DateIn)+1
WHEN B.M = MONTH(DateOut) THEN DAY(DateOut)
ELSE ND
END No_of_days
FROM TABLE_A A
INNER JOIN Month_Wise_Day B ON B.M BETWEEN MONTH(DateIn) AND MONTH (DateOut)
)A
GROUP BY A.Year, A.Month

Month number retention cohort calculation issue with Redshift

I'm trying to calculate user retention cohorts in redshift by month for the last 9 months. But I'm running into an issue where the month cohorts in the below query aren't being rolled into the correct month.
The data types I'm querying are:
userid - varchar
activated-varchar
Here is the query I'm trying to run:
with by_month as
(SELECT
userid
DATE_TRUNC('month', cast ("activated" as date)) AS joined_month
FROM customers
GROUP BY 1, 2),
first_month as
(select userid,
joined_month,
FIRST_VALUE(order_month) OVER (PARTITION BY userid ORDER BY
joined_month asc rows unbounded preceding) AS first
FROM by_month),
months as (select userid,
joined_month,
first,
extract(month from (joined_month - first_month)) as month_number
from first_month)
SELECT
first as "cohort",
SUM(CASE WHEN month_number = '0' THEN 1 ELSE 0 END) AS " Month 0",
SUM(CASE WHEN month_number = '1' THEN 1 ELSE 0 END) AS " Month 1",
SUM(CASE WHEN month_number = '2' THEN 1 ELSE 0 END) AS " Month 2",
SUM(CASE WHEN month_number = '3' THEN 1 ELSE 0 END) AS " Month 3",
SUM(CASE WHEN month_number = '4' THEN 1 ELSE 0 END) AS " Month 4",
SUM(CASE WHEN month_number = '5' THEN 1 ELSE 0 END) AS " Month 5",
SUM(CASE WHEN month_number = '6' THEN 1 ELSE 0 END) AS " Month 6",
SUM(CASE WHEN month_number = '7' THEN 1 ELSE 0 END) AS " Month 7",
SUM(CASE WHEN month_number = '8' THEN 1 ELSE 0 END) AS " Month 8",
SUM(CASE WHEN month_number = '9' THEN 1 ELSE 0 END) AS " Month 9"
from months
where first_month >= '2018-08-01'
GROUP BY 1
ORDER BY 1 desc
When I get the results back, I get an impossible number for a couple of cohorts:
Such as:
Cohort Month 0 Month 1
'2019-01-01' 95 120
I did some digging and found the month numbers aren't being counted correctly For instance, for the cohort of '2019-01-01 the month_ number is t's capturing 0,1, and 3 correctly but 2 is being miss-attributed to month 1. Any help on the fix would be much appreciated thank you!
Now, try
SELECT userid, joined_month, first_month, month_number FROM months
WHERE first = '2019-01-01'
(feel free to add other columns to drill down to the problem) add activated, order_month, etc until you get the right handle on what is causing it.

Group by year in sql

I am trying to group by year but was not able to do.I can get the column count but not year wise. this is what i tried.
select t_contract ,
sum(CASE t_contract when '18' then 1 else 0 end) as XL,
sum(CASE t_contract when '01' then 1 else 0 end) as VC,
sum(CASE t_contract when '75' then 1 else 0 end) as AN,
sum(CASE t_contract when '48' then 1 else 0 end) as CS
from icps.dbo.tickets
WHERE
t_date_time_issued >= DATEADD(year, -6, GETDATE())
GROUP BY contract
.. but i want to add year .. where i have t_date_time _issued column.
My another query is I have a column called t_zone_name and I want to sum all the rows where t_zone_anme like '%ICeland%' an i tried this:
sum(CASE t_zone_name like '%ICeland%' then 1 else 0 end) as ICELAND
but I get an error on statement like... thanks in advance.
LIKE
YEAR XL VC AN CS total
2010 50 50 50 50 200
2011 5 5 5 5 20
Try the below query:
SELECT t_contract, YEAR(t_date_time_issued) As Yr, SUM(CASE WHEN t_zone_name like '%ICeland%' THEN 1 ELSE 0 END) AS ICELAND
SUM(CASE t_contract when '18' then 1 else 0 end) as XL,
SUM(CASE t_contract when '01' then 1 else 0 end) as VC,
SUM(CASE t_contract when '75' then 1 else 0 end) as AN,
SUM(CASE t_contract when '48' then 1 else 0 end) as CS
FROM icps.dbo.tickets
WHERE YEAR(t_date_time_issued) >= (YEAR(GetDate()) - 6)
GROUP BY t_contract, YEAR(t_date_time_issued)
You might need change the order of t_contract and YEAR(t_date_time_issued) depending on which grouping you want to apply first.
As suggested by #ray I have replaced DATEPART(yyyy, t_date_time_issued) >= DATEPART(yyyy, DATEADD(year, -6, GETDATE())) with year(t_date_time_issued) >= (year(GetDate()) - 6)
If you want to group by year, in sql server, you might
GROUP BY DATEDIFF(year,t_date_time_issued, GETDATE())
In other DB engine, usually has method to get year part, or use substring to get year part from a time string.

Oracle opening and closing balance - SQL or PL/SQL needed?

select year,
month ,
d.PROD_ID,
T.CUSTOMER_ID,
SUM(CASE WHEN D.OP_TYPE = 1 THEN d.qty END) EARNED,
SUM(CASE WHEN D.OP_TYPE = 2 THEN d.qty END) SPEND
FROM TXN_HEADER T ,
TXN_DETAIL d ,
CUSTOMER A,
PRODUCT e
WHERE T.AMOUNT > 0
AND A.TYPE = 0
AND T.CUSTOMER_ID = A.CUSTOMER_ID
AND T.TXN_PK = D.TXN_PK
and d.PROD_ID = e.PROD_ID
and e.unit = 0
group by year, month ,d.PROD_ID, T.CUSTOMER_ID
ORDER BY 1,2,3,4
Output is as follows (here opening and closing not generated by query, but I required that has to be from the query)
YEAR MONTH PROD CUSTOMER OPENING EARNED SPEND CLOSING
---- ----- ---- -------- ------- ------ ----- -------
2012 8 548 12033 0 8 2 6
2012 9 509 12033 0 24 0 24
2012 9 509 12047 0 14 0 14
2012 9 548 12033 6 1 0 7
2012 9 548 12047 0 1 0 1
I required to generate the output as above. Here PROD_ID,CUSTOMER_ID wise dynamically the prev closing balance to be populated as opening and it shoulde calculate closing balance (opening+earned-spend) monthwise,customer wise ,product wise. is it possible to write in SQL or need to go PL/SQL?
I'd use analytics, with PROD_ID and CUSTOMER_ID in the partition clause to avoid mixing products and customers.
WITH
MONTHLY_BALANCE AS
(
SELECT
YEAR,
MONTH,
D.PROD_ID,
T.CUSTOMER_ID,
SUM(CASE WHEN D.OP_TYPE = 1 THEN D.QTY ELSE NULL END) EARNED,
SUM(CASE WHEN D.OP_TYPE = 2 THEN D.QTY ELSE NULL END) SPEND,
FROM TXN_HEADER T
JOIN CUSTOMER A
ON T.CUSTOMER_ID = A.CUSTOMER_ID
JOIN TXN_DETAIL D
ON T.TXN_PK = D.TXN_PK
JOIN PRODUCT E
ON D.PROD_ID = E.PROD_ID
WHERE T.AMOUNT > 0
AND A.TYPE = 0
AND E.UNIT = 0
GROUP BY YEAR, MONTH, D.PROD_ID, T.CUSTOMER_ID
)
SELECT
YEAR,
MONTH,
PROD_ID,
CUSTOMER_ID,
SUM(NVL(EARNED, 0) - NVL(SPEND, 0)) OVER(PARTITION BY PROD_ID, CUSTOMER_ID ORDER BY YEAR, MONTH ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) OPENING,
EARNED,
SPEND,
SUM(NVL(EARNED, 0) - NVL(SPEND, 0)) OVER(PARTITION BY PROD_ID, CUSTOMER_ID ORDER BY YEAR, MONTH ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT_ROW) CLOSING
FROM MONTHLY_BALANCE
ORDER BY 1, 2, 3, 4
Your CASE needs an ELSE
CASE WHEN D.OP_TYPE = 1 THEN d.qty ELSE 0 END
Without the else the CASE will return NULL when D.OP_TYPE is not equal to 1, and anything+NULL=NULL. When your WHEN is not satisfied it returns NULL and that is why you do not see anything for those columns.
To get OPENING and CLOSING calculated as you may want to use analytic functions like LEAD and LAG.
Select year,month,prod_id,customer_id,
LAG(closing,1,0) OVER (order by year,month,prod_id,customer_id) as opening,
earned,spend
,(LAG(closing,1,0) OVER (order by year,month,prod_id,customer_id)+closing) as closing
from (WITH temp AS (select year,
month ,
d.PROD_ID,
T.CUSTOMER_ID,
0 OPEN,
SUM(CASE WHEN D.OP_TYPE = 1 THEN d.qty END) EARNED,
SUM(CASE WHEN D.OP_TYPE = 2 THEN d.qty END) SPEND,
0 CLOSE
FROM TXN_HEADER T ,
TXN_DETAIL d ,
CUSTOMER A,
PRODUCT e
WHERE T.AMOUNT > 0
AND A.TYPE = 0
AND T.CUSTOMER_ID = A.CUSTOMER_ID
AND T.TXN_PK = D.TXN_PK
and d.PROD_ID = e.PROD_ID
and e.unit = 0
group by year, month ,d.PROD_ID, T.CUSTOMER_ID
ORDER BY 1,2,3,4)
SELECT year,month,prod_id,customer_id,open,earned,spend,(open+earned-spend) as closing
from temp);