I have to list items that have not been updated for a multiple of two years after their last update. This is to run as a cron job once a day.
I know I can do this with something ugly like:
SELECT art_id, art_update FROM items
WHERE art_update = now()::date - interval '2 years'
OR art_update = now()::date - interval '4 years'
OR art_update = now()::date - interval '6 years'
OR art_update = now()::date - interval '8 years'
OR art_update = now()::date - interval '10 years';
Is there any way to avoid this by checking for a modulo interval? Or some other generalised way to express this?
select art_id, art_update
from items
where art_update in (
(now() - interval '2 years')::date,
(now() - interval '4 years')::date,
(now() - interval '6 years')::date,
(now() - interval '8 years')::date,
(now() - interval '10 years')::date
);
or
select art_id, art_update
from items
where art_update in (
select d::date
from generate_series (
now() - interval '2 years',
now() - interval '10 years',
- interval '2 years'
) d(d)
);
http://www.postgresql.org/docs/current/static/functions-srf.html
You can try this.
SELECT art_id, art_update
FROM items
Where int4(date_part('year', art_update)) % 2 = 0;
You can generate a series of dates at 2 year intervals going back from today (to 10 years ago in the below) and join this back to your table:
SELECT i.art_id, i.art_update
FROM items i
INNER JOIN generate_series(2, 10, 2) s (years)
ON i.art_update = now()::date - interval '1 years' * s.years;
Example on SQL Fiddle
N.B This appears to be marginally faster if you generate the dates in the series, rather than numbers:
SELECT i.art_id, i.art_update
FROM items i
INNER JOIN generate_series(now() - interval '10 years',
now() - interval '2 years',
interval '2 years') d (d)
ON art_update = d.d::date;
Related
Hey Pros,
I am far away to have good knowledge about SQL, and would ask you to give me some hints.
Currently we aggregate our data with python and I would try to switch this when possible to. (SQL (Postgresql server)
My goal is to have one statment that generate an average for two seperates column's for specific time intervals (1 Hour, 1 Day, 1 Week, Overall) also all events in each period shoud be counted.
I can create 4 single statments for each interval but strugle how to combine this four selects into on result set.
select
count(id) as hour_count,
camera_name,
round(avg("pconf")) as hour_p_conf,
round(avg("dconf")) as hour_d_conf
from camera_events where timestamp between NOW() - interval '1 HOUR' and NOW() group by camera_name;
select
count(id) as day_count,
camera_name,
round(avg("pconf")) as day_p_conf,
round(avg("dconf")) as day_d_conf
from camera_events where timestamp between NOW() - interval '1 DAY' and NOW() group by camera_name;
select
count(id) as week_count,
camera_name,
round(avg("pconf")) as week_p_conf,
round(avg("dconf")) as week_d_conf
from camera_events where timestamp between NOW() - interval '1 WEEK' and NOW() group by camera_name;
select
count(id) as overall_count,
camera_name,
round(avg("pconf")) as overall_p_conf,
round(avg("dconf")) as overall_d_conf
from camera_events group by camera_name;
When possbile the result should look like the data on image
Some hints would be great, thank u
Consider conditional aggregation by moving WHERE logic to CASE statements in SELECT. Alternatively, in PostgreSQL use FILTER clauses.
select
camera_name,
count(id) filter(timestamp between NOW() - interval '1 HOUR' and NOW()) as hour_count,
round(avg("pconf") filter(timestamp between NOW() - interval '1 HOUR' and NOW())) as hour_p_conf,
round(avg("dconf") filter(timestamp between NOW() - interval '1 HOUR' and NOW())) as hour_d_conf,
count(id) filter(timestamp between NOW() - interval '1 DAY' and NOW()) as day_count,
round(avg("pconf") filter(timestamp between NOW() - interval '1 DAY' and NOW())) as day_p_conf,
round(avg("dconf") filter(timestamp between NOW() - interval '1 DAY' and NOW())) as day_d_conf,
count(id) filter(timestamp between NOW() - interval '1 WEEK' and NOW()) as week_count,
round(avg("pconf") filter(timestamp between NOW() - interval '1 WEEK' and NOW())) as week_p_conf,
round(avg("dconf") filter(timestamp between NOW() - interval '1 WEEK' and NOW())) as week_d_conf,
count(id) as overall_count,
round(avg("pconf")) as overall_p_conf,
round(avg("dconf")) as overall_d_conf
from camera_events
group by camera_name;
The simplest way is to join them. For example:
select
coalesce(h.camera_name, d.camera_name, w.camera_name) as camera_name
h.hour_count, h.hour_p_conf, h.hour_d_conf
d.day_count, d.day_p_conf, d.day_d_conf
w.week_count, w.week_p_conf, w.week_d_conf
from (
-- hourly query here
) h
full join (
-- daily query here
) d on d.camera_name = h.camera_name
full join (
-- weekly query here
) w on w.camera_name = coalesce(h.camera_name, d.camera_name)
I have a simple problem, I do not know where I am doing wrong. I really appreciate your help. Thanks in advance for any hint.
I have data and need to split the release date up by within 1 month, 2-3 months, 4-5 months, 6-7 months, 7-9 months, and so on.
So, I have created a query ;
Select case
when release_date >= current_date - interval '1 month' then 'Within 1 month'
when release_date between (current_date - interval '2 months') and (current_date - interval '3 months') then '2-3 months'
when release_date between (current_date - interval '4 months') and (current_date - interval '5 months') then '4-5 months'
when release_date between (current_date - interval '6 months') and (current_date - interval '7 months') then '6-7 months'
when release_date between (current_date - interval '8 months') and (current_date - interval '9 months') then '8-9 months'
when release_date between (current_date - interval '10 months') and (current_date - interval '12 months') then '9-12 months'
when release_date < current_date - interval '12 months' then '> 12 month'
end as release_date,
country,
....
at the end I can only see within 1 month, NULL and > 12 month.
I think your output is generating a calculation other than "months". The answers depend on which dbms you are using. This is one way to perform in postgres, but if you are using another dbms, then you should be able to easily change the syntax for calculating months in the CTE. Using the CTE enables you to have a clean case statement.
with calculated_months as
(
select release_date,
extract(year from age(current_date, release_date)) * 12
+ extract(month from age(current_date, release_date))
+ 1 as months_from_current
from t1
)
select t.release_date,
case
when c.months_from_current < 2 then 'Within 1 month'
when c.months_from_current between 2 and 3 then '2-3 months'
when c.months_from_current between 4 and 5 then '4-5 months'
when c.months_from_current between 6 and 7 then '6-7 months'
when c.months_from_current between 8 and 9 then '8-9 months'
when c.months_from_current between 10 and 12 then '10-12 months'
when c.months_from_current > 12 then '12+ months'
end months_since_release,
t.country
from t1 t
join calculated_months c
on c.release_date = t.release_date
db-fiddle here
EDIT: Not sure how your data appear, but you may want to add a "distinct" in the CTE (select distinct release date...).
If I'm reading your query correctly, you need to swap the values in your "between" statements:
Select case
when release_date >= current_date - interval '1 month' then 'Within 1 month'
when release_date between (current_date - interval '3 months') and (current_date - interval '2 months') then '2-3 months'
when release_date between (current_date - interval '5 months') and (current_date - interval '4 months') then '4-5 months'
when release_date between (current_date - interval '7 months') and (current_date - interval '6 months') then '6-7 months'
when release_date between (current_date - interval '9 months') and (current_date - interval '8 months') then '8-9 months'
when release_date between (current_date - interval '12 months') and (current_date - interval '9 months') then '9-12 months'
when release_date < current_date - interval '12 months' then '> 12 month'
end as release_date,
country,
I do not have a system to run this on to confirm, but it should work.
The issue is how between works, BETWEEN (lower_limit) AND (upper_limit).
I am trying to run this query was able to till some time ago. I don't know what went wrong and I started getting this error now?
Your database returned: ERROR: set-returning functions are not allowed in CASE Hint: You might be able to move the set-returning function into a LATERAL FROM item.
My query:
SELECT distinct
(CASE
WHEN {PERIOD} = 'Previous Quarter' AND pto.pto_start_date < (date_trunc('quarter', now() - INTERVAL '1 month') + INTERVAL '1 month')::date AND pto.pto_end_date >= (date_trunc('quarter', now() - INTERVAL '1 month') + INTERVAL '1 month')::date
THEN generate_series(pto.pto_start_date, pto.pto_end_date, '2 day'::interval)
WHEN {PERIOD} = 'Current Quarter' AND pto.pto_start_date < (date_trunc('quarter', now() - INTERVAL '1 month') + INTERVAL '1 month')::date AND pto.pto_end_date >= (date_trunc('quarter', now() - INTERVAL '1 month') + INTERVAL '1 month')::date
THEN generate_series(pto.pto_start_date, pto.pto_end_date, '1 day'::interval)
ELSE
generate_series(pto.pto_start_date, pto.pto_end_date, '1 day'::interval)
END) AS dt
FROM cust_pto pto
Start dates and end Dates:
What has gone wrong?
Why you're getting the error now: you upgraded to postgres 10. Set returning functions are no longer allowed.
What to do: there is more than one way to accomplish what you're trying to do. For the sake of keeping it as close as possible to your original query, all you have to do is put your CASE statement inside generate_series:
SELECT distinct generate_series(
pto.pto_start_date,
pto.pto_end_date,
CASE
WHEN {PERIOD} = 'Previous Quarter' AND pto.pto_start_date < (date_trunc('quarter', now() - INTERVAL '1 month') + INTERVAL '1 month')::date AND pto.pto_end_date >= (date_trunc('quarter', now() - INTERVAL '1 month') + INTERVAL '1 month')::date THEN
'2 day'::interval
ELSE
'1 day'::interval
END
) AS dt
FROM cust_pto pto
I'm trying to grab all the appointments that have been booked for this current week I initially tried this:
select *
from appointments
where staff_id = 'id'
and (to_timestamp(appointments.start_time)) > (current_date - interval '1 week')
and (to_timestamp(appointments.start_time)) < (current_date + interval '1 week')
This just selects all the appointments that equal +/- 7 days, so not the current week. This was my next try. I know its broken but I think it gets the point across.
select *
from appointments
where staff_id = 'id'
and (to_timestamp(appointments.start_time)) > (current_date - interval concat(extract(dow from current_date), ' days'))
and (to_timestamp(appointments.start_time)) < (current_date + interval concat(7 - extract(dow from current_date), ' days'))
How would I go about building this query?
If you agree with Postgres's definition of a week, then you can do:
select a.*
from appointments a
where staff_id = 'id' and
to_timestamp(appointments.start_time) >= date_trunc('week', current_date) and
to_timestamp(appointments.start_time) < date_trunc('week', current_date) + interval '1 week';
I am trying to pull daily data using PostgreSQL. My problem is that the day seems to 'reset' at around 5 PM (Los Angeles Time). Is there a workaround this problem? Here is my query:
SELECT COUNT(distinct be.booking_id)AS "Number of Bookings Today"
FROM booking_events be
WHERE be.event IN ('approve') AND
be.created_at >= current_date AND
be.created_at < current_date + interval '1 day';
You can use hours to offset the current date. I think the logic is:
SELECT COUNT(distinct be.booking_id) as "Number of Bookings Today"
FROM booking_events be
WHERE be.event IN ('approve') AND
be.created_at >= current_date - interval '7 hour' AND
be.created_at < current_date + interval '1 day' - interval '7 hour';