SELECT data grouped by WEEK in SQL - sql

I have a working query (ORACLE SQL) that gives me gallons grouped by store number, with gallons summed by type and a percentage column as well. Each store number has a different conversion date from which I sum up the data -
SELECT StoreNbr,
SUM(CASE WHEN(ClrntSys IN ('844', '84448')) THEN Gallons ELSE 0 END) AS Gallons_844,
SUM(CASE WHEN(ClrntSys ='GIC') THEN Gallons ELSE 0 END) AS Gallons_GIC,
SUM(CASE WHEN(ClrntSys IN ('844', '84448', 'GIC')) THEN Gallons ELSE 0 END) AS Total_Gallons,
CONCAT(CAST((SUM(CASE WHEN(ClrntSys ='GIC') THEN Gallons ELSE 0 END) /
SUM(CASE WHEN(ClrntSys IN ('844', '84448', 'GIC')) THEN Gallons ELSE 0 END)) AS DECIMAL (5,2)) * 100, '%') AS Percent_GIC
FROM MQ_CDS_NETTRAN
WHERE ClrntSys IN ('844', '84448', 'GIC')
AND ((CostCenter = '701104' AND LastTranDate >= DATE '2020-03-10')
OR (CostCenter = '701109' AND LastTranDate >= DATE '2020-03-04')
OR (CostCenter = '701257' AND LastTranDate >= DATE '2020-03-12'))
GROUP BY StoreNbr
ORDER BY StoreNbr;
I now need to also sum it up by week, with Sunday being the first day of each week, but I'm having trouble understanding how DATEPART works. Just so I could get a better idea, I tried only to sum up the data for last week using DATEPART examples I'm seeing online, but this doesn't work. It's giving me "invalid identifier DATEPART". -
SELECT DATEPART(week, 5/17/20) AS weekTotal,
StoreNbr,
SUM(CASE WHEN(ClrntSys IN ('844', '84448')) THEN Gallons ELSE 0 END) AS Gallons_844,
SUM(CASE WHEN(ClrntSys ='GIC') THEN Gallons ELSE 0 END) AS Gallons_GIC,
SUM(CASE WHEN(ClrntSys IN ('844', '84448', 'GIC')) THEN Gallons ELSE 0 END) AS Total_Gallons,
CONCAT(CAST((SUM(CASE WHEN(ClrntSys ='GIC') THEN Gallons ELSE 0 END) /
SUM(CASE WHEN(ClrntSys IN ('844', '84448', 'GIC')) THEN Gallons ELSE 0 END)) AS DECIMAL (5,2)) * 100, '%') AS Percent_GIC
FROM MQ_CDS_NETTRAN
WHERE ((CostCenter = '701104' AND LastTranDate >= DATE '2020-03-10')
OR (CostCenter = '701109' AND LastTranDate >= DATE '2020-03-04')
OR (CostCenter = '701257' AND LastTranDate >= DATE '2020-03-12'))
GROUP BY DATEPART(week, 5/17/20), StoreNbr
ORDER BY StoreNbr;
What I really need, however, is each week's data (with Sunday being the beginning of each week) summed up separately going back to each store's conversion date. Is it possible to do that? Is there something else besides DATEPART that would work better?

Sorry - just noticed that you said Oracle SQL, and my first answer was for SQL Server! The reason you are getting an error is that DATEPART is not an Oracle function. Instead, you can simply do math on the dates, using a known sunday (prior to a known first date in the DB table) as an anchor date:
SELECT
'30-DEC-2018' as "Known Sunday,
trunc((sysdate - to_date('30-DEC-2018')) / 7) as "Week Num",
to_date('30-DEC-2018')
+ (trunc((sysdate - to_date('30-DEC-2018')) / 7) * 7)"
FROM
dual

Related

DB2 use of labeled duration not valid with multiple date intervals

I'm trying to refactor a MySQL query to run on DB2/iSeries and I'm getting the error Use of labeled duration not valid.
Looking at the documentation I feel like the usage below should be working.
Am I missing something?
SELECT
IFNULL(SUM(CASE WHEN CURDATE() BETWEEN n.start_date AND n.expire_date
THEN 1 ELSE 0 END), 0) AS current,
IFNULL(SUM(CASE WHEN CURDATE() - 365 DAY BETWEEN n.start_date AND n.expire_date
THEN 1 ELSE 0 END), 0) AS prior,
IFNULL(SUM(CASE WHEN '2018-12-31' - 7 DAY BETWEEN n.start_date AND n.expire_date
THEN 1 ELSE 0 END), 0) AS full
FROM salesnumbers;
The issue is likely your date intervals. Try using CURRENT DATE instead of CURDATE(). Also, you may list date intervals +/- some amount directly in DB2.
SELECT
COUNT(CASE WHEN CURRENT DATE BETWEEN n.start_date AND n.expire_date
THEN 1 END) AS current,
COUNT(CASE WHEN CURRENT DATE - 1 YEAR BETWEEN n.start_date AND n.expire_date
THEN 1 END) AS prior,
COUNT(CASE WHEN DATE('2018-12-31') - 7 DAY BETWEEN n.start_date AND n.expire_date
THEN 1 END) AS full
FROM salesnumbers;
Note that I replaced your conditional sums with conditional counts. This leaves the code slightly more terse, because we don't have to spell out an explicit ELSE condition (the default being NULL).

Using multiple sum case lines in query

This code does exactly what I need it to do for the desired months. Basically provides Numerator and Denominator.
SUM(CASE WHEN smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date BETWEEN '2013-01-01' and '2013-01-31' THEN 1 ELSE 0 END) AS Total_Jan13,
SUM(CASE WHEN smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date BETWEEN '2013-01-01' and '2013-01-31' and [ind_ra_cfvmc_00-30] = 'YES' THEN 1 ELSE 0 END) AS RA_Jan13,
SUM(CASE WHEN smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date BETWEEN '2013-02-01' and '2013-02-28' THEN 1 ELSE 0 END) AS Total_Feb13,
SUM(CASE WHEN smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date BETWEEN '2013-02-01' and '2013-02-28' and [ind_ra_cfvmc_00-30] = 'YES' THEN 1 ELSE 0 END) AS RA_Feb13,
SUM(CASE WHEN smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date BETWEEN '2013-03-01' and '2013-03-31' THEN 1 ELSE 0 END) AS Total_Mar13,
It can get pretty tedious when you have multiple months...is there a more efficient way of performing this calculation?
THanks!
You could group them by the year and month in a sub-select and then manipulate that:
SELECT DATEPART(year, smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date) AS TotalYear,
DATEPART(month, smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date) AS TotalMonth,
COUNT(*) AS Total
FROM [table]
GROUP BY DATEPART(year, smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date),
DATEPART(month, smsdss.c_cfv_pas_fct_pt_acct_vst_all.vst_end_date)
and that would give you the totals by month, without having to specify the months beforehand.

Dynamically Creating Column Counts Per Day of Month

OK, so I have this query to find number of types per day like below. (Courtesy of MarkD here Counting Items/Rows in DB as Columns Grouped by Another Column)
select type,
sum(case when MyDate = '' then 1 else 0 end) as "10/1",
sum(case when MyDate = '' then 1 else 0 end) as "10/2
...etc
from MyTabe
group by type
However, I want to dynamically create date columns and have the count generated for each column, otherwise I would end up manually adding a new column for each day of the month.
I guess the best way to get the output I wanted was to do the following:
define startDate = to_date('2013-10-01', 'yyyy-mm-dd')
select type,
sum(case when MyDate = &&startDate then 1 else 0 end) as "1"
, sum(case when MyDate = &&startDate +1 then 1 else 0 end) as "2"
, sum(case when MyDate = &&startDate +2 then 1 else 0 end) as "3"
, sum(case when MyDate = &&startDate +3 then 1 else 0 end) as "4"
...etc for as many days of current month I am running
, sum(case when MyDate = &&startDate +29 then 1 else 0 end) as "30"
, sum(case when MyDate = &&startDate +30 then 1 else 0 end) as "31"--This would be commented out for Nov
from MyTabe
group by type
order by type
;
This way if I want to run this for Nov, Dec, Jan, and so on, I can just change the variable at the top and run the query. This is what I was looking for; however, I still wonder if it would be possible to generate the columns dynamically, but the more I look at it, the more I think that would require a pivot table.

SQL - Pivot/Unpivot?

I have a table in SQL Server 2008 in the format as below(simplified format) :
Date Miles Cost
I want to get a result like
Previous Week Current Week Variance
Miles
Cost
How can I achieve this? Do I need pivot/unpivot or what is the easiest and performance effective way to achieve this. Thanks.
SELECT 'Miles' AS Type, SUM(CASE WHEN date = lastweek THEN miles ELSE 0 END) as lastweek
, SUM(CASE WHEN date = thisweek THEN miles ELSE 0 END) as thisweek
SUM(CASE WHEN date = lastweek THEN miles ELSE 0 END) - SUM(CASE WHEN date = thisweek THEN miles ELSE 0 END) as variance
FROM yourtable
UNION
SELECT 'Cost' AS Type, SUM(CASE WHEN date = lastweek THEN cost ELSE 0 END) as lastweek
, SUM(CASE WHEN date = thisweek THEN cost ELSE 0 END) as thisweek
SUM(CASE WHEN date = lastweek THEN cost ELSE 0 END) - SUM(CASE WHEN date = thisweek THEN cost ELSE 0 END) as variance
FROM yourtable
This isn't 100% correct, but the syntax should be correct and should get you pretty close to what you're looking for

SQL Buckets Determine Age Grouping

I have a database with legacy data that stores transactions and uses a "bucket" method for determining account balances. I need a way to get aged past due for accounts.
Table Transactions
TransactionId
TransactionType (CHARGE,RECEIPT)
Amount
PostDate
To get the current balance:
SELECT SUM(CASE TransactionTypeId WHEN RECEIPT THEN Amount * -1 ELSE Amount END) CurrentBalance
I need a way to determine past due 30, 60, 90, 120, etc:
Account Current 30 60 90 120+
12345 $50.00 $0.00 $25.00 $25.00 $0.00
I tried running separate queries and limiting the CHARGE postdates to greater than 30,60,90,120, running for each group and subtracting the others, etc but cannot get the expected results.
The table doesn't store a flag for past due, all balances are calculated on the fly.
Am I missing something simple? I tried a net search but not sure if there's a term for this type of sql query.
Database is SQL Server if that helps.
TIA
You could use an additional clause in the case to filter out transactions from the last 30 days. For example:
SELECT
SUM(
CASE WHEN TransactionTypeId = 'RECEIPT' THEN -Amount
ELSE Amount
END) as CurrentDue
, SUM(CASE WHEN datediff(d,PostDate,getdate()) <= 30 THEN 0
WHEN TransactionTypeId = 'RECEIPT' THEN -Amount
ELSE Amount
END) as PastDue30
, ...
FROM Transactions
To just exclude charges from the past 30 days, swap the when clauses:
, SUM(CASE WHEN TransactionTypeId = 'RECEIPT' THEN -Amount
WHEN datediff(d,PostDate,getdate()) <= 30 THEN 0
ELSE Amount
END) as PastDue30
This is what I ended up with, something that I had from before, but was missing the checks for when the amount due is zero, as well as checking if the prior group had a negative value. I had to add them because I was getting strange results, say if the account was overpaid for a service, which would have a negative due for the prior period.
SELECT
ServiceId,
AmountDue PastDue,
CASE AmountDue WHEN 0 THEN 0 ELSE CASE WHEN AmountDue60 < 0 THEN 0 ELSE AmountDue30 - AmountDue60 END END PastDue30,
CASE AmountDue WHEN 0 THEN 0 ELSE CASE WHEN AmountDue90 < 0 THEN 0 ELSE AmountDue60 - AmountDue90 END END PastDue60,
CASE AmountDue WHEN 0 THEN 0 ELSE CASE WHEN AmountDue120 < 0 THEN 0 ELSE AmountDue90 - AmountDue120 END END PastDue90,
CASE AmountDue WHEN 0 THEN 0 ELSE CASE WHEN AmountDue120 < 0 THEN 0 ELSE AmountDue120 END END PastDue120
FROM
(
SELECT T.ServiceId,
SUM(CASE WHEN T.TransactionTypeId = #Receipt THEN T.TAmount * -1 WHEN T.TransactionTypeId = #Charge THEN T.TAmount ELSE 0 END) AmountDue,
SUM(CASE WHEN T.TransactionTypeId = #Receipt THEN T.TAmount * -1 WHEN T.TransactionTypeId = #Charge THEN CASE WHEN DATEDIFF(D, T.TPostDate, #TPostDate) >= 30 THEN T.TAmount ELSE 0 END ELSE 0 END) AmountDue30,
SUM(CASE WHEN T.TransactionTypeId = #Receipt THEN T.TAmount * -1 WHEN T.TransactionTypeId = #Charge THEN CASE WHEN DATEDIFF(D, T.TPostDate, #TPostDate) >= 60 THEN T.TAmount ELSE 0 END ELSE 0 END) AmountDue60,
SUM(CASE WHEN T.TransactionTypeId = #Receipt THEN T.TAmount * -1 WHEN T.TransactionTypeId = #Charge THEN CASE WHEN DATEDIFF(D, T.TPostDate, #TPostDate) >= 90 THEN T.TAmount ELSE 0 END ELSE 0 END) AmountDue90,
SUM(CASE WHEN T.TransactionTypeId = #Receipt THEN T.TAmount * -1 WHEN T.TransactionTypeId = #Charge THEN CASE WHEN DATEDIFF(D, T.TPostDate, #TPostDate) >= 120 THEN T.TAmount ELSE 0 END ELSE 0 END) AmountDue120
FROM Transactions T
WHERE T.AccountId = #AccountId
GROUP BY T.ServiceId
) AB