Query to get sales from this month and previous month - sql

We'd like to get total sales for this month and previous month. The query is:
SELECT sum(CASE
WHEN date_trunc('month', date_start)= date_trunc('month', now()) THEN sales
ELSE NULL
END) AS curr_sales,
sum(CASE
WHEN date_trunc('month', date_start)= date_trunc('month', now()- interval '1' MONTH) THEN sales
ELSE NULL
END) AS pr_sales
FROM sales
But it returned this error:
"Specified types or functions (one per INFO message) not supported on Redshift tables. We're running Postgresql 8.0.2. Any ideas? Thanks!

Use date_trunc('month', current_date) instead of date_trunc('month', now()) to get current month in redshift.
now() is not a supported function in redshift but current_date will return a date in the current session time zone (UTC by default) in the default format: YYYY-MM-DD.
UPDATE
Query w/ Sample Data >>
with sales(sales, date_start) as(
select 1 , current_date union
select 2 , current_date union
select 2 , current_date - interval '1' month union
select 3 , current_date - interval '1' month
)
SELECT sum(CASE
WHEN date_trunc('month', date_start)= date_trunc('month', current_date) THEN sales
ELSE NULL
END) AS curr_sales,
sum(CASE
WHEN date_trunc('month', date_start)= date_trunc('month', current_date - interval '1' MONTH) THEN sales
ELSE NULL
END) AS pr_sales
FROM sales;
And results are coming as expected:
curr_sales pr_sales
3 5

Related

CASE WHEN function & date function to change now() > to an assumption date

WITH latest AS (
SELECT
DISTINCT customer_id,
MAX(submitted_on) AS latest_order
FROM orders
GROUP BY 1
),
AA AS (
SELECT
DISTINCT o.customer_id,
latest.latest_order,
now() - INTERVAL '91 days' AS reference_more_than_90D,
now() - INTERVAL '31 days' AS reference_more_than_31D,
now() - INTERVAL '30 days' AS reference_more_than_30D
FROM orders AS o
LEFT JOIN latest
ON o.customer_id = latest.customer_id
GROUP BY 1,2,3,4,5
)
SELECT
DISTINCT o.customer_id,
latest.latest_order,
AA.reference_more_than_30D,
AA.reference_more_than_31D,
AA.reference_more_than_90D,
CASE
WHEN latest.latest_order >= AA.reference_more_than_31D THEN 'r'
WHEN latest.latest_order <= AA.reference_more_than_30D THEN 'a'
ELSE 'l'
END AS status
FROM orders AS o
LEFT JOIN latest
ON o.customer_id = latest.customer_id
LEFT JOIN AA
ON o.customer_id = AA.customer_id
With above this is the output
Seems that my CASE WHEN function isn't working right as 2021-04-29 is an older date than 2022-06-17/2022-06-16 and 2022-04-17 > The status should reflect 'l'
how should i change now() > to an assumption date like 2021-07-01 in this case ? Taking into consideration i only have 2021 & 2020 orders to look at
Thanks
Seems that my CASE WHEN function isn't working right as 2021-04-29 is an older date than 2022-06-17/2022-06-16 and 2022-04-17 > The status should reflect 'l'.
I think you don't need to use those left joins. Please refer to the below script.
WITH latest AS (
SELECT
DISTINCT customer_id,
MAX(submitted_on) AS latest_order
FROM orders
GROUP BY 1
),
AA AS (
SELECT
DISTINCT customer_id,
latest_order,
now() - INTERVAL '91 days' AS reference_more_than_90D,
now() - INTERVAL '31 days' AS reference_more_than_31D,
now() - INTERVAL '30 days' AS reference_more_than_30D
FROM latest
GROUP BY 1,2,3,4,5
)
SELECT
DISTINCT customer_id,
latest_order,
reference_more_than_30D,
reference_more_than_31D,
reference_more_than_90D,
CASE
WHEN latest_order >= reference_more_than_31D THEN 'r'
WHEN latest_order <= reference_more_than_30D THEN 'a'
ELSE 'l' END
AS status
FROM AA
How should I change now() > to an assumption date like 2021-07-01 in this case ? Taking into consideration, I only have 2021 & 2020 orders to look at.
\set variable
hank=> \set name hank
hank=> \set time '2018-02-06 10:09:00'
hank=> select * from tb2 where c2=:'name' and c3>=:'time';
c1 | c2 | c3
----+------+----------------------------
1 | hank | 2018-02-06 10:08:00.78750
How can I set now() to '2021-07-01' ? . I need to make an assumption of the current date to be '2021-07-01'.
CREATE OR REPLACE FUNCTION get_data (v_Date TIMESTAMP WITHOUT TIME ZONE)
RETURNS TABLE (
latest_order TIMESTAMP WITHOUT TIME ZONE,
reference_more_than_30D TIMESTAMP WITHOUT TIME ZONE,
reference_more_than_31D TIMESTAMP WITHOUT TIME ZONE,
reference_more_than_90D TIMESTAMP WITHOUT TIME ZONE
)
AS $$
BEGIN
RETURN QUERY SELECT v_Date,
v_Date - INTERVAL '30 days',
v_Date - INTERVAL '31 days',
v_Date - INTERVAL '91 days';
END;
$$ LANGUAGE plpgsql;
select * from get_data('2021-07-01');
I would suggest avoiding the use of now() as this includes millisecond precision. Instead you probably only need date precision so you could use current_date instead. Plus you really don't need select distinct at all, nor do you need multiple joins etc.
WITH AA
AS (
SELECT customer_id
, CURRENT_DATE - INTERVAL '91 days' AS reference_more_than_90D
, CURRENT_DATE - INTERVAL '31 days' AS reference_more_than_31D
, CURRENT_DATE - INTERVAL '30 days' AS reference_more_than_30D
, MAX(submitted_on) AS latest_order
FROM orders
GROUP BY customer_id
)
SELECT AA.customer_id
, AA.latest_order
, AA.reference_more_than_30D
, AA.reference_more_than_31D
, AA.reference_more_than_90D
, CASE
WHEN AA.latest_order >= AA.reference_more_than_31D
THEN 'r'
WHEN AA.latest_order <= AA.reference_more_than_30D
THEN 'a'
ELSE 'l'
END AS STATUS
FROM AA
If you ned to use a preset date instead of current_date then just use a date literal instead e.g.
SELECT
customer_id
, '2021-07-01'::timestamp - INTERVAL '91 days' AS reference_more_than_90D
, '2021-07-01'::timestamp - INTERVAL '31 days' AS reference_more_than_31D
, '2021-07-01'::timestamp - INTERVAL '30 days' AS reference_more_than_30D
, MAX(submitted_on) AS latest_order
FROM orders
GROUP BY customer_id
)
SELECT AA.customer_id
, AA.latest_order
, AA.reference_more_than_30D
, AA.reference_more_than_31D
, AA.reference_more_than_90D
, CASE
WHEN AA.latest_order >= AA.reference_more_than_31D
THEN 'r'
WHEN AA.latest_order <= AA.reference_more_than_30D
THEN 'a'
ELSE 'l'
END AS STATUS
FROM AA
see this db<>fiddle here

get List of counts from table based on dates in sql

I have to fetch List of counts from table by department here is my table structure
empid empname department departmentId joinedon
i want to populate all the joined employee on today , yesterday and More than 2 days like [12,25,89] i.e
12* joined today
25 joined yesterday
81 joined all prior to yesterday(2+day)
* 0 if there isn't any entries for given date range.
You would use aggregation on a case expression:
select (case when joinedon::date = current_date then 'today'
when joinedon::date = current_date - interval '1 day' then 'yesterday'
when joinedon::date < current_date - interval '1 day' then 'older'
end) as grp,
count(*)
from t
group by grp;
In additional to #Gordon Linoff answer:
SELECT
days.day,
coalesce(t.cnt, 0) count
FROM (
SELECT * FROM (VALUES ('today'), ('yesterday'), ('older')) AS days (day)
)days
LEFT JOIN (
SELECT (CASE WHEN joinedon::date = current_date THEN 'today'
WHEN joinedon::date = current_date - interval '1 day' THEN 'yesterday'
WHEN joinedon::date < current_date - interval '1 day' THEN 'older'
end) as day,
count(*) cnt
FROM t
GROUP BY day
) t on t.day = days.day;
Test it here
You can use the group by as follows:
select department,
(case when joinedon::date = current_date then 'today'
when joinedon::date = current_date - interval '1 day' then 'yesterday'
when joinedon::date < current_date - interval '1 day' then 'More than 2 days'
end) as grp,
Coalesce(count(*),0)
from t
group by grp, department;

Customizing the range of a week with date_trunc

I've been trying for hours now to write a date_trunc statement to be used in a group by where my week starts on a Friday and ends the following Thursday.
So something like
SELECT
DATE_TRUNC(...) sales_week,
SUM(sales) sales
FROM table
GROUP BY 1
ORDER BY 1 DESC
Which would return the results for the last complete week (by those standards) as 09-13-2019.
You can subtract 4 days and then add 4 days:
SELECT DATE_TRUNC(<whatever> - INTERVAL '4 DAY') + INTERVAL '4 DAY' as sales_week,
SUM(sales) as sales
FROM table
GROUP BY 1
ORDER BY 1 DESC
The expression
select current_date - cast(cast(7 - (5 - extract(dow from current_date)) as text) || ' days' as interval);
should always give you the previous Friday's date.
if by any chance you might have gaps in data (maybe more granular breakdowns vs just per week), you can generate a set of custom weeks and left join to that:
drop table if exists sales_weeks;
create table sales_weeks as
with
dates as (
select generate_series('2019-01-01'::date,current_date,interval '1 day')::date as date
)
,week_ids as (
select
date
,sum(case when extract('dow' from date)=5 then 1 else 0 end) over (order by date) as week_id
from dates
)
select
week_id
,min(date) as week_start_date
,max(date) as week_end_date
from week_ids
group by 1
order by 1
;

Oracle SQL - How to retrieve the ID Count difference between today vs yesterday

I have a table that captures when a customer purchases a product. It captures a unique purchase id along with a timestamp of when the purchase was made.
I want to be able to query, the difference between how many purchases were taken today vs yesterday?
Not sure how to query this on oracle?
You can use conditional aggregation:
select sum(case when trunc(datecol) = trunc(sysdate - 1) then 1 else 0 end) as num_yesterday,
sum(case when trunc(datecol) = trunc(sysdate) then 1 else 0 end) as num_today,
sum(case when trunc(datecol) = trunc(sysdate) then 1
when trunc(datecol) = trunc(sysdate - 1) then -1
end) as diff
from t
where datecol >= trunc(sysdate - 1);
you can use the Group function to grouping the purchase day with timestamp information and count the purchase id.
select trunc(purchase_ts) Day, count(purchase_id) Count
from purchase
group by trunc(purchase_ts)
order by 1
Using TRUNC on the column will prevent Oracle from using an index on that column (instead you would need a separate function-based index); instead use a CASE statement to test whether the date is between the start of the day and the start of the next day and then COUNT the values between those ranges:
SELECT COUNT(
CASE
WHEN TRUNC( SYSDATE ) - INTERVAL '1' DAY <= your_date_column
AND your_date_coumn < TRUNC( SYSDATE )
THEN 1
END
) AS count_for_yesterday,
COUNT(
CASE
WHEN TRUNC( SYSDATE ) <= your_date_column
AND your_date_coumn < TRUNC( SYSDATE ) + INTERVAL '1' DAY
THEN 1
END
) AS count_for_today
FROM your_table
WHERE TRUNC( SYSDATE ) - INTERVAL '1' DAY <= your_date_column
AND your_date_coumn < TRUNC( SYSDATE ) + INTERVAL '1' DAY

Select data with a rolling date criteria

The below query returns a distinct count of 'members' for a given month and brand (see image below).
select to_char(transaction_date, 'YYYY-MM') as month, brand,
count(distinct UNIQUE_MEM_ID) as distinct_count
from source.table
group by to_char(transaction_date, 'YYYY-MM'), brand;
The data is collected with a 15 day lag after the month closes (meaning September 2016 MONTHLY data won't be 100% until October 15). I am only concerned with monthly data.
The query I would like to build: Until the 15th of this month (October), last month's data (September) should reflect August's data. The current partial month (October) should default to the prior month and thus also to the above logic.
After the 15th of this month, last month's data (September) is now 100% and thus September should reflect September (and October will reflect September until November 15th, and so on).
The current partial month will always = the prior month. The complexity of the query is how to calc prior month.
This query will be ran on a rolling basis so needs to be dynamic.
To be clear, I am trying to build a query where distinct_count for the prior month (until end of current month + 15 days) should reflect (current month - 2) value (for each respective brand). After 15 days of the close of the month, prior month = (current month - 1).
Partial current month defaults to prior month's data. The 15 day value should be variable/modifiable.
First, simplify the query to:
select to_char(transaction_date, 'YYYY-MM') as month, brand,
count(distinct members) as distinct_count
from source.table
group by members, to_char(transaction_date, 'YYYY-MM'), brand;
Then, you are going to have a problem. The problem is that one row (say from Aug 20th) needs to go into two groups. A simple group by won't handle this. So, let's use union all. I think the result is something like this:
select date_trunc('month', transaction_date) as month, brand,
count(distinct members) as distinct_count
from source.table
where (date_trunc('month', transaction_date) < date_trunc('month' current_date) - interval '1 month') or
(day(current_date) > 15 and date_trunc('month', transaction_date) = date_trunc('month' current_date) - interval '1 month')
group by date_trunc('month', transaction_date), brand
union all
select date_trunc('month' current_date) - interval '1 month' as month, brand,
count(distinct members) as distinct_count
from source.table
where (day(current_date) < 15 and date_trunc('month', transaction_date) = date_trunc('month' current_date) - interval '1 month')
group by brand;
Since you already have a working query, I concentrate on the subselect. The condition you can use here is CASE, especially "Searched CASE"
case
when extract(day from current_date) < 15 then
extract(month from current_date - interval '2 months')
else
extract(month from current_date - interval '1 month')
end case
This may be used as part of a where clause, for example.
Here is some sudo code to get the begin date and the end date for your interval.
Begin date:
date DATE_TRUNC('month', CURRENT_DATE - integer 15) - interval '1 month'
This will return the current month only after the 15th day, from there you can subtract a full month to get your starting point.
End Date:
To calculate this, grab the begin date, plus a month, minus a day.
If the source table is partitioned by transaction_date, this syntax (not masking transaction_date with expression) enables partitions eliminatation.
select to_char(transaction_date, 'YYYY-MM') as month
,count (distinct members) as distinct_count
,brand as brand
FROM source.table
where transaction_date between date_trunc('month', current_date) - case when extract (day from current_date) >= 15 then 1 else 2 end * interval '1' month
and date_trunc('month', current_date) - case when extract (day from current_date) >= 15 then 0 else 1 end * interval '1' month - interval '1' day
group by to_char(transaction_date, 'YYYY-MM')
,brand
;