DB2 issue with CASE and adding days to date - sql

I'm trying to get a selection of records where the date field of the record(s) is current date + 7 Days
I was previously doing this in MySQL like so:
SUM(CASE WHEN ( d.next_date + INTERVAL 7 DAY) THEN 1 ELSE 0 END) AS dateCount
But now trying to do this on DB2 it's not working
SUM(case WHEN f.next_date + 7 days then 1 else 0 end) as dateCount
I'm wondering why this would be, but I'm simply trying to get a count of all records where the date is today + 7 days.
What am I doing wrong

Your case expression is lacking a comparison, e.g. when (some_value is compared to other_value) then do_something .... Perhaps an equal?
SUM(case when {date field of the record} = f.next_date + 7 days then 1 else 0 end) as dateCount

Try this:
select sum(case when f.next_date = current date + 7 day then 1 end) as dateCount
from table(values
current date + 1 day
, current date + 7 day
, current date + 7 day
) f(next_date);

Related

Oracle SQL Show all month of a year, with or without value ORA-01841

I have a problem with which I despair, I have data distributed over days, and would like to display this for the entire year in months and once in weeks.
My problem with the months that I get in the select my data displayed (for January, September) but I want that all months for a selected year are displayed, even if they are empty. For this I have made myself a "WITH" (copied) and now try to join this, but get an ORA-01841 error.
And how do I implement the whole construct to display only the weeks.
WITH MONAT_ZAEHLER (MZ) AS
(
SELECT
TO_CHAR(ADD_MONTHS(TO_DATE('01.2022','MM.YYYY'),LEVEL -1),'Month', 'NLS_DATE_LANGUAGE = GERMAN') AS GRD_ROW_ID
FROM
DUAL
CONNECT BY LEVEL <= 12
)
SELECT
TO_CHAR(GEN_DATUM,'Month', 'NLS_DATE_LANGUAGE = GERMAN') AS GRD_ROW_ID
, COUNT( DISTINCT CASE
WHEN LP_BELEGUNG.ART = 1 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS "1"
, COUNT( DISTINCT CASE
WHEN LP_BELEGUNG.ART = 2 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS "2"
, COUNT( DISTINCT CASE
WHEN LP_BELEGUNG.ART = 3 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS "3"
, COUNT( DISTINCT CASE
WHEN LP_BELEGUNG.ART = 99 THEN LP_BELEGUNG.LP_BELEGUNG_ID
ELSE NULL
END ) AS "99"
FROM
LP_BELEGUNG
FULL OUTER JOIN MONAT_ZAEHLER ON TRUNC(LP_BELEGUNG.GEN_DATUM, 'Month') = MONAT_ZAEHLER.MZ
WHERE
TO_CHAR(GEN_DATUM, 'YYYY') = '2022'
GROUP BY
TO_CHAR(GEN_DATUM,'Month', 'NLS_DATE_LANGUAGE = GERMAN')
The error is because you're converting the month to a name string in the CTE, then trying to convert it again for the GRD_ROW_ID alias.
The solution is basically the same as your previous question, but now you want the CTE to have one row per month - which you are doing, but you should leave it as a date type in the CTE, not convert it to a string there:
with cte (dt) as (
select add_months(date '2022-01-01', level - 1)
from dual
connect by level <= 12
)
... then convert that actual date value to a string:
SELECT
TO_CHAR(cte.dt, 'Month', 'NLS_DATE_LANGUAGE = GERMAN') AS GRD_ROW_ID
...
... and outer join to your actual table as before, using a date range:
FROM
cte
LEFT JOIN
LP_BELEGUNG
ON
LP_BELEGUNG.GEN_DATUM >= cte.dt AND LP_BELEGUNG.GEN_DATUM < add_months(cte.dt, 1)
GROUP BY
cte.dt
ORDER BY
cte.dt
... this time looking for values where the the GEN_DATUM is greater than or equal to cte.dt value (again, as before), which is midnight on the first day of the first day of the month; and less than add_months(cte.dt, 1), which is midnight on the first day of the first day of the following month. So for January, that will be >= 2022-01-01 00:00:00 and < 2022-02-01 00:00:00, which is all possible dates and times during that month.
GRD_ROW_ID
ANZAHL_ART_1
ANZAHL_ART_2
ANZAHL_ART_3
ANZAHL_ART_4
Januar
0
0
0
0
Februar
0
0
0
0
März
0
0
0
0
April
0
0
0
0
Mai
0
0
0
0
Juni
0
0
0
0
Juli
0
0
0
0
August
0
0
0
0
September
1
1
1
7
Oktober
0
0
0
0
November
0
0
0
0
Dezember
0
0
0
0
fiddle
To get a row for every week of the year you would do something similar again, but in blocks of 7 days:
with cte (dt) as (
select date '2022-01-01' + 7 * (level - 1)
from dual
connect by level <= 53
)
SELECT
TO_CHAR(cte.dt, 'YYYY-WW') AS GRD_ROW_ID
...
FROM
cte
LEFT JOIN
LP_BELEGUNG
ON
LP_BELEGUNG.GEN_DATUM >= cte.dt AND LP_BELEGUNG.GEN_DATUM < cte.dt + 7
AND LP_BELEGUNG.GEN_DATUM < add_months(trunc(cte.dt, 'YYYY'), 12)
GROUP BY
cte.dt
ORDER BY
cte.dt
which has an extra check in the join to stop it including data from week 53 which is actually in the following year - which I'm guessing you woudl want to do.
fiddle

Percentage Difference Using CASE WHEN clause

The table I am working with is called 'transactions'. The columns are id (customer id), amount (amount spent by customer), timestamp (time of purchase).
I am trying to query:
yesterdays revenue: sum of amount.
percent difference from 8 day's ago revenue to yesterday's revenue.
MTD.
percent difference from last months MTD to this months MTD.
SAMPLE DATA
id
amount
timestamp
1
50
2021-12-01
2
60
2021-12-02
3
70
2021-11-05
4
80
2022-01-26
5
90
2022-01-25
6
20
2022-01-26
7
80
2022-01-19
EXPECTED OUTPUT
yesterday_revenue
pct_change_week_ago
mtd
pct_change_month_prior
100
0.25
270
0.50
This is my code. The percent change columns are both incorrect. Please help.
select
-- yesterday
sum(case when timestamp::date = current_date - 1 then amount else null end) yesterday_revenue,
-- yesterday v. last week
(sum(case when timestamp::date > current_date - 1 then amount else null end) - sum(case when timestamp::date = current_date - 8 then amount else null end))
/ sum(case when timestamp::date = current_date - 8 then amount else null end) pct_change_week_ago,
-- mtd
sum(case when date_trunc('month',timestamp) = date_trunc('month',CURRENT_DATE -1) then amount else null end) mtd,
-- mtd v. month prior
(sum(case when date_trunc('month',timestamp) = date_trunc('month',CURRENT_DATE -1) then amount else null end) - sum(case when date_trunc('month',timestamp) = date_trunc('month',CURRENT_DATE -1) - interval '1 month'
and date_part('day',timestamp ) <= date_part('day', CURRENT_DATE -1) then amount else null end))
/ sum(case when date_trunc('month',timestamp) = date_trunc('month',CURRENT_DATE -1) - interval '1 month'
and date_part('day',timestamp ) <= date_part('day', CURRENT_DATE -1) then amount else null end) pct_change_month_prior
from transactions
Some things to consider:
"yesterday vs last week" currently uses timestamp::date > current_date - 1 at the start. This will include transactions from today only, not yesterday (it says "greater than yesterday"). I think it should be timestamp::date = current_date - 1
I could be wrong here, but I think sum(case when date_trunc('month',timestamp) = date_trunc('month',CURRENT_DATE -1) will capture transactions on the current date as well if the current date is in the same month as yesterday. You may not want that.
As far as I can tell, 'pct_change_month_prior' should be 1.45, not 0.5. You have 110 in December and 270 in January. 270 - 110 = 160 and 160 / 110 = 1.45. Your existing query already returns that result. FWIW, you can also use new/old-1 to get the same result in a slightly simpler way.
OK, so really, it's about your maths in the SELECT part of your statement.
Amount changed is: new - old
As a multiplicative amount, it is (new - old) / old or new / old - 1
As a percentage, you need to multiply by 100... 100 * (new / old - 1) but I understand you aren't worried about this.
Further to this, let's make sure your new and old are correct.
Yesterday's sum:
sum(case when timestamp::date = CURRENT_DATE - 1 then amount else null end)
8 days ago sum:
sum(case when timestamp::date = CURRENT_DATE - 8 then amount else null end)
1 month to yesterday sum:
sum(case when timestamp::date > CURRENT_DATE - 1 - INTERVAL '1 month' AND timestamp::date <= CURRENT_DATE - 1 then amount else null end)
1 month to 1 month and 1 day ago sum:
sum(case when timestamp::date > CURRENT_DATE - 1 - INTERVAL '2 month' AND timestamp::date <= CURRENT_DATE - 1 - INTERVAL '1 month' then amount else null end)
Start of month to yesterday sum:
sum(case when timestamp::date > DATE_TRUNC('month', CURRENT_DATE - 1) AND timestamp::date <= CURRENT_DATE - 1 then amount else null end)
Start of yesterday's last month to a month ago yesterday sum:
sum(case when timestamp::date > DATE_TRUNC('month', CURRENT_DATE - 1 - INTERVAL '1 month') AND timestamp::date <= CURRENT_DATE - 1 - INTERVAL '1 month' then amount else null end)
It's important you don't change = to > or cropping to just the date part else you will include more than you really want.
Effectively, by cropping to the month part, it would almost always sum all transactions for two months.

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.

Counts based on hour SQL

How can I get the count per hour?
select count_hr_1, count_hr_2, count_hr_3 from db.table where year=2018 and month=01 and day=02 and hour=01 OR hour=02 OR hour=03;
This SQL/Query is probably invalid but I want to get the counts of hour 1,2,3
If you are able to process the resultset as multiple rows instead of one, you could use GROUP BY:
select hour, count(*)
from db.table
where
year = 2018
and month = 1
and day = 2
and hour in (1, 2, 3)
group by hour
select sum(case when hour = 1 then 1 else 0 end) as count_hr_1,
sum(case when hour = 2 then 1 else 0 end) as count_hr_2,
sum(case when hour = 3 then 1 else 0 end) as count_hr_3
from db.table
where year = 2018
and month = 1
and day = 2
and hour in (1,2,3)

How to show different dates data (from the same table) as columns in Oracle

I'm sorry if the title wasn't too clear, but the following explanation will be more accurate.
I have the following view:
DATE USER CONDITION
20140101 1 A
20140101 2 B
20140101 3 C
20140108 1 C
20140108 3 B
20140108 2 C
What I need to do is present how many users where in all conditions this week and 7 days before today.
Output should be like this:
Condition Today Last_Week (Today-7)
A 0 1
B 1 1
C 2 1
How can I do this in Oracle? I will need to do this for 4 weeks so itll be Today-7,14-21.
I've tried this with group by but I get the "week2" as rows. Then I've tried something like Select conditions, (select count(users) from MyView where DATE='Today') FROM MyView(looking at something thats actually working) but it doesnt work for me.
Achieved this with a little modification of the accepted answer:
select condition,
count(case when to_date(xdate) = to_date(sysdate) then 1 end) to_day,
count(case when to_date(xdate) = to_date(sysdate-7) then 1 end) last_7_days
from my_table
group by condition
select condition, count(case when to_date(xdate) = to_date(sysdate) then 1 end) to_day,
count(case when to_date(xdate) < to_date(sysdate) then 1 end) last_7_days
from my_table
where to_date(xdate) >= to_date(sysdate) - 7
group by condition
select condition
, sum
( case
when date between trunc(sysdate) - 7 and trunc(sysdate) - 1
then 1
else 0
end
)
last_week
, sum
( case
when date between trunc(sysdate) and trunc(sysdate + 1)
then 1
else 0
end
)
this_week
from table
group
by condition
By using the conditional count (as a sum) and grouping on condition you can filter out all desired dates. Note that using trunc will cause to use the begin of the day.