Optimizing a postgres query with date range - postgresql

Is there any way to optimize this query in postgres (version 14)? I have an index on entry_date and have tried to index entry_date::date but it did not seem to matter either.
SELECT
user_name,
extract(year from entry_date) as year,
extract(month from entry_date) as month,
extract(day from entry_date) as day,
extract(hour from entry_date) as hour,
extract(minute from entry_date) as minute,
count(*) as cnt
FROM pdq.q_1
WHERE
entry_date > current_timestamp - interval '10' day
GROUP BY
user_name,
year,
month,
day,
hour,
minute
Here is the explain analyze plan

Related

How to get value previous month & week?

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

PostgreSQL: How to get last 2 years of data from today's date

I am using PostgreSQL and need to know how to get data from last 2 years starting today, which is current_date.
Is my query correct ?
select count(*) from table_name where
creation_date >= date_trunc('year', now()) - interval '2' year and
creation_date < date_trunc('year', now());
Assuming you want a two year span starting from the current date:
select count(*) from table_name where
creation_date >= current_date - interval '2' year and
creation_date < current_date;
Using current_date eliminates the need to truncate now().
What About Extracting only Year from the two parts and make just little comparaison like that;
select count(*) from table_name where
EXTRACT(YEAR FROM CURRENT_DATE) - EXTRACT(YEAR FROM creation_date) BETWEEN 0 and 2

Make date_trunc() start on Sunday instead of Monday

Select date_trunc('week',dateTime) Date_week, Max(Ranking) Runing_Total_ID
from (select datetime, id , dense_rank () over (order by datetime) as Ranking
from Table1)
group by 1
This query is working for me to give me the running total of total IDs by week. But the week starts on Monday in Postgres by default. Is there any way to change the week start to SUNDAY?
Shift the timestamp back and forth:
Add a day before feeding the timestamp to date_trunc(), then subtract again:
SELECT date_trunc('week', datetime + interval '1 day') - interval '1 day' AS date_week
, max(ranking) AS runing_total_id
FROM (
SELECT datetime, dense_rank() OVER (ORDER BY datetime) AS ranking
FROM table1
) sub
GROUP BY 1;
See:
PostgreSQL custom week number - first week containing Feb 1st

Fetch records of current month using PostgreSQL query

Suppose I have following data in a table
id createdAt
1 2021-02-26T06:29:03.482Z
2 2021-02-27T06:29:03.482Z
3 2021-03-14T06:29:03.482Z
4 2021-03-17T06:29:03.482Z
I want data of current month. ie, if I generate report in march, I need to fetch results of march, so we need only current month data from table.
wanted output is
id createdAt
3 2021-03-14T06:29:03.482Z
4 2021-03-17T06:29:03.482Z
Anyone please help. Thank you.
You can use date_trunc():
select *
from the_table
where date_trunc('month', createdat) = date_trunc('month', current_timestamp);
date_trunc('month', ...) returns the first day of the month.
However, the above is not able to make use of an index on createdat. To improve performance, use a range query:
select *
from the_table
where createdat >= date_trunc('month', current_timestamp)
and createdat < date_trunc('month', current_timestamp) + interval '1 month'
The expression date_trunc('month', current_timestamp) + interval '1 month' returns the start of the next month (that's way this is compared with <)
You can compare the month and year of a date with the current one. But the index by field will not be used, you can build a separate index by year and month for this.
select *
from your_table
where extract(YEAR FROM createdAt) = extract(YEAR FROM now())
and extract(MONTH FROM createdAt) = extract(MONTH FROM now())

Postgres count by date with timezone

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.