Right now I'm getting an average for each month
SELECT EXTRACT(MONTH FROM date_time) AS month,
EXTRACT(YEAR FROM date_time) AS year,
avg("total")
FROM my_table
GROUP BY EXTRACT(MONTH FROM date_time), EXTRACT(YEAR FROM date_time)
But the SQL query needs to adjust so the total value current month - previous month
Is it possible?
For weekly
SELECT EXTRACT(WEEK FROM date_time) AS week,
EXTRACT(YEAR FROM date_time) AS year,
avg("total")
FROM my_table
GROUP BY EXTRACT(WEEK FROM date_time), EXTRACT(YEAR FROM date_time)
Yes, it's possible:
SELECT t1.month, t2.year, t1.tot - t2.tot FROM
(
SELECT EXTRACT(MONTH FROM date_time) AS month, EXTRACT(YEAR FROM date_time) AS year, avg("total") AS tot
FROM my_table GROUP BY EXTRACT(MONTH FROM date_time), EXTRACT(YEAR FROM date_time)
) t1
join (
SELECT EXTRACT(MONTH FROM date_time) AS month, EXTRACT(YEAR FROM date_time) AS year, avg("total") AS tot
FROM my_table GROUP BY EXTRACT(MONTH FROM date_time), EXTRACT(YEAR FROM date_time)
) t2
on ((t1.year = t2.year) and (t1.month = t2.month + 1)) or
((t1.year = t2.year + 1) and (t1.month = 1) and (t2.month = 12))
I have taken your select and converted it into two subselects, named them as t1 and t2 respectively and joined them by the criteria of left join.
Note that the very first month will not have a pair currently and if you need it nevertheless, then you can use left join and coalesce to make sure that even an unpaired item has a "pair" and a NULL for tot is defaulted to 0.
Note further that you can convert this subquery to a view for better readability.
If I get that correctly, you can first group avg(total) by yer and month, and the use LAG() window function to get previous month value, something like:
with my_table(date_time, total) as (
values
('2022-03-29', 10),
('2022-04-29', 12),
('2022-05-30', 20),
('2022-05-31', 30)
)
,grouped as (
SELECT EXTRACT('MONTH' FROM date_time::timestamp) AS month, EXTRACT('YEAR' FROM date_time::timestamp) AS year, avg("total") AS total
FROM my_table
GROUP BY EXTRACT('MONTH' FROM date_time::timestamp) , EXTRACT('YEAR' FROM date_time::timestamp)
)
SELECT *, LAG(total) OVER(ORDER BY year, month) as prev_month_total
FROM grouped
Related
I want to be able to generate groups of row by days, weeks, month or depending on the interval I set
Following this solution, it works when granularity is by month. But trying the interval of 1 week, no records are being returned.
This is the rows on my table
This is the current query I have for per month interval, which works perfectly.
SELECT *
FROM (
SELECT day::date
FROM generate_series(timestamp '2018-09-01'
, timestamp '2018-12-01'
, interval '1 month') day
) d
LEFT JOIN (
SELECT date_trunc('month', created_date)::date AS day
, SUM(escrow_amount) AS profit, sum(total_amount) as revenue
FROM (
select distinct on (order_id) order_id, escrow_amount, total_amount, create_time from order_item
WHERE created_date >= date '2018-09-01'
AND created_date <= date '2018-12-01'
-- AND ... more conditions
) t2 GROUP BY 1
) t USING (day)
ORDER BY day;
Result from this query
And this is the per week interval query. I will reduce the range to two months for brevity.
SELECT *
FROM (
SELECT day::date
FROM generate_series(timestamp '2018-09-01'
, timestamp '2018-11-01'
, interval '1 week') day
) d
LEFT JOIN (
SELECT date_trunc('week', created_date)::date AS day
, SUM(escrow_amount) AS profit, sum(total_amount) as revenue
FROM (
select distinct on (order_id) order_id, escrow_amount, total_amount, create_time from order_item
WHERE created_date >= date '2018-09-01'
AND created_date <= date '2018-11-01'
-- AND ... more conditions
) t2 GROUP BY 1
) t USING (day)
ORDER BY day;
Take note that I have records from October, but the result here doesn't show anything for October dates.
Any idea what I am missing here?
Results from your first query are not truncated to the begin of the week.
date_trunc('2018-09-01'::date, 'week')::date
is equal to
'2018-08-27'::date
so your join using day is not working
'2018-09-01'::date <> '2018-08-27'::date
Your query should look more like that:
SELECT *
FROM (
SELECT day::date
FROM generate_series(date_trunc('week',timestamp '2018-09-01') --series begin trunc
, timestamp '2018-11-01'
, interval '1 week') day
) d
LEFT JOIN (
SELECT date_trunc('week', created_date::date)::date AS day
, SUM(escrow_amount) AS profit, sum(total_amount) as revenue
FROM (
select distinct on (order_id) order_id, escrow_amount, total_amount, create_time from order_item
WHERE created_date::date >= date '2018-09-01'
AND created_date::date <= date '2018-11-01'
-- AND ... more conditions
) t2 GROUP BY 1
) t USING (day)
WHERE day >= '2018-09-01' --to skip days from begining of the week to the begining of the series before trunc
ORDER BY day;
I'm doing some cohort analysis and want to see for a group of customers in November, how many transact weekly, fortnightly, and monthly; and for how long
I have this for the week and month (weekly example):
WITH weekly_users AS (
SELECT user_fk
, DATE_TRUNC('week',created_at) AS week
, (DATE_PART('year', created_at) - 2016) * 52 + DATE_PART('week', created_at) - 45 AS weeks_between
FROM transactions
WHERE created_at >= '2016-11-01' AND created_at < '2017-12-01'
GROUP BY user_fk, week, weeks_between
),
t2 AS (
SELECT weekly_users.*
, COUNT(*) OVER (PARTITION BY user_fk
ORDER BY week ROWS BETWEEN UNBOUNDED PRECEDING
AND 1 PRECEDING) AS prev_rec_cnt
FROM weekly_users
)
SELECT week
, COUNT(*)
FROM t2
WHERE weeks_between = prev_rec_cnt
GROUP BY week
ORDER BY week;
But weekly is too little of an interval, and monthly too much. So I want fortnight. Has anyone done this before? From Googling it seems like a challenge
Thanks in advance
Just worked it out, this is how you'd do it:
WITH fortnightly_users AS (
SELECT user_fk
, EXTRACT(YEAR FROM created_at) * 100 + CEIL(EXTRACT(WEEK FROM created_at)/2) AS fortnight
, (EXTRACT(YEAR FROM created_at) - 2016) * 26 + CEIL(EXTRACT(WEEK FROM created_at)/2) - 23 AS fortnights_between
FROM transactions
WHERE created_at >= '2016-11-01' AND created_at < '2017-12-01'
GROUP BY user_fk, fortnight, fortnights_between
),
t2 AS (
SELECT fortnightly_users.*
, COUNT(*) OVER (PARTITION BY user_fk
ORDER BY fortnight ROWS BETWEEN UNBOUNDED PRECEDING
AND 1 PRECEDING) AS prev_rec_cnt
FROM fortnightly_users
)
SELECT fortnight
, COUNT(*)
FROM t2
WHERE fortnights_between = prev_rec_cnt
GROUP BY fortnight
ORDER BY fortnight;
So you get the week number, then divide by 2. Rounding up to avoid fractional numbers for fortnights
I want to query a table to find out a count of objects created by date, day and month in Postgres.
Fetch count for last 30 days
SELECT d.date, count(se.id)
FROM (SELECT to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD') AS date
FROM generate_series(0, 30) AS offs) d LEFT OUTER JOIN
someTable se
ON d.date = to_char(date_trunc('day', se.created_at), 'YYYY-MM-DD')
GROUP BY d.date;
Fetch count by day
select to_char(created_at,'day') as Day,
extract(month from created_at) as Date,
count("id") as "Count"
from someTable
group by 1,2
Fetch count by month
select to_char(created_at,'Mon') as mon,
extract(year from created_at) as yyyy,
count("id") as "Count"
from someTable
group by 1,2
This works fine for me. The problem that I have is, I want the data to be fetched based on different timezones. I have stored the time in UTC. I would be able to run these queries with different timezones.
What is the best way to do it?
Check this answer to get the datetime in Postgres with different timezone.
Fetch count for last 30 days
SELECT d.date, count(se.id)
FROM (SELECT to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD') AS date
FROM generate_series(0, 30) AS offs) d LEFT OUTER JOIN
someTable se
ON d.date = to_char(date_trunc('day', se.created_at::timestamp with time zone at time zone 'EST'), 'YYYY-MM-DD')
GROUP BY d.date;
Fetch count by day
select to_char(created_at::timestamp with time zone at time zone 'EST','day') as Day,
extract(month from created_at) as Date,
count("id") as "Count"
from someTable
group by 1,2
Fetch count by month
select to_char(created_at::timestamp with time zone at time zone 'EST','Mon') as mon,
extract(year from created_at) as yyyy,
count("id") as "Count"
from someTable
group by 1,2
Also refer to this Postgres documentation to learn about timezone with datetime.
I can easily get total sales in this month and previous month.
SELECT ‘This Mount’, SUM(Price) FROM Sales
WHERE EXTRACT(MONTH FROM OrderDate) = EXTRACT(MONTH FROM CURRENT_DATE)
AND EXTRACT(YEAR FROM OrderDate) = EXTRACT(YEAR FROM CURRENT_DATE)
Union All
SELECT ‘Previous Month’, SUM(Price) FROM Sales
WHERE EXTRACT(MONTH FROM OrderDate) = EXTRACT(MONTH FROM CURRENT_DATE)
AND EXTRACT(YEAR FROM OrderDate) = EXTRACT(YEAR FROM CURRENT_DATE)
I want to get the total sales in this quarter and previous quarter.
Getting quarter from a date is very easy with MS-SQL as follows:
SELECT DATEPART(QUARTER, #date)
How can I do this with Firebird?
Use DECODE function in conjunction with EXTRACT:
SELECT
DECODE(EXTRACT(MONTH FROM <date_field>),
1, 'I',
2, 'I',
3, 'I',
4, 'II',
5, 'II',
6, 'II',
7, 'III',
8, 'III',
9, 'III',
'IV')
FROM
<some_table>
Or just
SELECT
(EXTRACT(MONTH FROM <date_field>) - 1) / 3 + 1
FROM
<some_table>
SELECT dates,
EXTRACT(MONTH from dates) as SalesMonth,
floor(((EXTRACT(MONTH from dates)-1) / 3.0 + 1)) as QTR
from CustomerPO
where ((dates > '1/1/2016') and (dates < '12/31/2016'))
order by dates
Here, 'dates' is the field name of Order table 'CustomerPO'
SELECT dates,
EXTRACT(MONTH from dates) as SalesMonth,
ceil(EXTRACT(MONTH from dates) / 3) as QTR
from CustomerPO
where ((dates > '1/1/2016') and (dates < '12/31/2016'))
order by dates
Here is my code but its showing null while today is friday. But I would like to get last working day.
-- Insert statements for procedure here
--Below is the param you would pass
DECLARE #dateToEvaluate date=GETDATE();
--Routine
DECLARE #startDate date=CAST('1/1/'+CAST(YEAR(#dateToEvaluate) AS char(4)) AS date); -- let's get the first of the year
WITH
tally(n) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))-1 FROM sys.all_columns),
dates AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS dt_id,
DATEADD(DAY,n,#startDate) AS dt,
DATENAME(WEEKDAY,DATEADD(DAY,n,#startdate)) AS dt_name
FROM tally
WHERE n<366 --arbitrary
AND DATEPART(WEEKDAY,DATEADD(DAY,n,#startDate)) NOT IN (6)
AND DATEADD(DAY,n,#startDate) NOT IN (SELECT CAST(HolidayDate AS date) FROM Holiday)),
curr_id(id) AS (SELECT dt_id FROM dates WHERE dt=#dateToEvaluate)
SELECT d.dt
FROM dates AS d
CROSS JOIN
curr_id c
WHERE d.dt_id+1=c.id
The code below will take any date and "walk backward" to find the previous week day (M-F) which is not in the #holidays table.
declare #currentdate datetime = '2015-03-22'
declare #holidays table (holiday datetime)
insert #holidays values ('2015-03-20')
;with cte as (
select
#currentdate k
union all
select
dateadd(day, -1, k)
from cte
where
k = #currentdate
or ((datepart(dw, k) + ##DATEFIRST - 1 - 1) % 7) + 1 > 5 --determine day of week independent of culture
or k in (select holiday from #holidays)
)
select min(k) from cte
The dates table doesn't have any FRIDAY dates in it. Change the NOT IN (6) to NOT IN (1, 7). This will remove Saturday and Sundays from the dates table.