Postgresql compare two select result on the same table - postgresql

I compare results from two selects and get 1 or 0 as a final result.
Below query syntax is good but this query causes timeout.
SELECT (CASE WHEN (
select count(*) from order where ordered_date > (NOW() - INTERVAL '120 minutes')
and order_ordered = current_date) >
(select count(*)/3
from order
where ordered_date > (NOW() - INTERVAL '2 days' - INTERVAL '120 minutes')
and ordered_date < (NOW() - INTERVAL '2 days'))
THEN 1 ELSE 0 end);
Therefore, i try to optimize the query to use an alias for each select as below :
select (case when a > b then 1 else 0 end) from (select count(*) from order where ordered_date > (NOW() - INTERVAL '120 minutes')
and order_ordered = current_date) as a,
from (select count(*)/3
from order
where ordered_date > (NOW() - INTERVAL '2 days' - INTERVAL '120 minutes')
and ordered_date < (NOW() - INTERVAL '2 days'))as b;
I have syntax error near "from", in my memory this kind of syntax works on mysql.
Could you please advise me if there a possiblity to use two times of "from" by using alias on Postgresql or if you know another possility i am a taker.
Sample:
First query gives : select count(*) from order where ordered_date > (NOW() - INTERVAL '120 minutes') and order_ordered = current_date -> 60
Seconde query gives : select count(*)/3 from order where ordered_date > (NOW() - INTERVAL '2 days' - INTERVAL '120 minutes') and ordered_date < (NOW() - INTERVAL '2 days') -> 20
Final condition : case when (60 > 20 then 1 else 0 end)
Result expected : 1
Thanks

I suggest using SELECT in WITH (here documentation).
WITH orders_current_date AS (
SELECT count(*)
FROM order
WHERE ordered_date > (NOW() - INTERVAL '120 minutes')
AND order_ordered = current_date)
), orders_interval AS (
SELECT count(*)/3
FROM order
WHERE ordered_date > (NOW() - INTERVAL '2 days' - INTERVAL '120 minutes')
AND ordered_date < (NOW() - INTERVAL '2 days')
)
SELECT
CASE
WHEN SELECT * FROM orders_current_date > SELECT * FROM orders_interval
THEN '1'
ELSE
0
END;

Related

Query max and min in a fixed range of time of each day interval POSTGRES

I have a query below to query max and min of day interval in a range of time ( current_date - 2 to current_date - 1). Now, I need to query dayshift and extra shift separately ( dayshift from 5am to 3pm, extra shift will be the remains).
select sum(gap) from (
select to_char(time_stamp, 'yyyy/mm/dd') as day,
EXTRACT(EPOCH FROM (max(time_stamp) - min(time_stamp))) /3600 as gap
from group_table_debarker
where time_stamp >= (current_date - 2)
and time_stamp <= (current_date - 1)
and to_char(time_stamp, 'hh:mi') > '03:00' and to_char(time_stamp, 'hh:mi') < '15:00'
group by to_char(time_stamp, 'yyyy/mm/dd')
) as xxx
select sum(gap) from (
select to_char(time_stamp, 'yyyy/mm/dd') as day,
EXTRACT(EPOCH FROM (max(time_stamp) - min(time_stamp))) /3600 as gap
from group_table_debarker
where time_stamp >= (current_date - 2)
and time_stamp <= (current_date - 1)
and to_char(time_stamp, 'hh:mi') > '03:00' and to_char(time_stamp, 'hh:mi') < '15:00'
group by to_char(time_stamp, 'yyyy/mm/dd')
) as xxx
I've tried this but result wasn't expected

How to use COALESCE with INTERVAL in PostgreSQL?

This is a syntax error, but didn't see what the correct syntax would be.
The goal is to handle the case where days is null.
SELECT * FROM tbl WHERE created_at > current_date - INTERVAL 'COALESCE(%days%, 999) DAY'
You can multiply COALESCE(%days%, 999) to an interval of 1 day:
SELECT * FROM tbl
WHERE created_at > current_date - COALESCE(%days%, 999) * INTERVAL '1 DAY'
Use make_interval()
SELECT *
FROM tbl
WHERE created_at > current_date - make_interval(days:=COALESCE(%days%, 999));

Speed up postgres read window query on overlapping date ranges

I have a table (simplified) that contains readings like follows
meter_id read_date value
1 2017-01-01 10
1 2017-01-15 15
1 2017-02-05 20
1 2017-04-15 22
2 2016-12-14 120
2 2016-03-02 200
This table contains millions of readings.
And I have a view (or query) that goes something like
select meter_id, read_date as start_read_date, value as start_value,
CASE
WHEN lead(read_date) OVER read_wdw IS NULL THEN date_trunc('month'::text, read_date + '1 day'::interval) + '1 mon'::interval - '1 day'::interval) + '1 mon'::interval - '1 day'::interval
ELSE lead(.read_date) OVER read_wdw::date
END::date AS read_end_date,
lead(value) OVER read_wdw AS end_value,
from reads_table
WINDOW read_wdw AS (PARTITION BY meter_id ORDER BY read_date);
I need to be able to query dates within a certain month. So start_read_date, end_read_date between e.g. '2017-01-01' and '2017-01-31'
So e.g.
select * from my_view where daterange(start_read_date,end_read_date, '[]') && daterange('2017-01-01', '2017-01-31', '[])
Which with the above table would return
meter_id start_read_date start_value end_read_date end_value
1 2017-01-01 10 2017-01-15 15
1 2017-01-15 15 2017-02-05 20
2 2016-12-14 120 2016-03-02 200
Is there a way to do a similar query on this table without having to build the whole view first to get my desired result?
Something like (which doesn't work)
select meter_id, read_date as start_read_date, value as start_value,
CASE
WHEN lead(read_date) OVER read_wdw IS NULL THEN date_trunc('month'::text, read_date + '1 day'::interval) + '1 mon'::interval - '1 day'::interval) + '1 mon'::interval - '1 day'::interval
ELSE lead(.read_date) OVER read_wdw::date
END::date AS read_end_date,
lead(value) OVER read_wdw AS end_value,
from reads_table
where read_date between '2017-01-01' and '2017-01-31'
or lead(read_date) over read_window between '2017-01-01' and '2017-01-31'
WINDOW read_wdw AS (PARTITION BY meter_id ORDER BY read_date);
Actually wrapping it in another select seems to resolve...
select * from (
select meter_id, read_date as start_read_date, value as start_value,
CASE
WHEN lead(read_date) OVER read_wdw IS NULL THEN date_trunc('month'::text, read_date + '1 day'::interval) + '1 mon'::interval - '1 day'::interval) + '1 mon'::interval - '1 day'::interval
ELSE lead(.read_date) OVER read_wdw::date
END::date AS read_end_date,
lead(value) OVER read_wdw AS end_value,
from reads_table
WINDOW read_wdw AS (PARTITION BY meter_id ORDER BY read_date)
)sub
where read_start_date between ...
or read_end_date between ...

Unify select sql. Postgres

I can unify the two select below in a single, where in the first column return the result of the first and second column the result of the second.
select count(*) from rrhh.empleado where fecha_contratado > current_date - interval '100 days'; // select1
select count(*) from rrhh.empleado where fecha_fin_contrato > current_date - interval '100 days'; //select2
Thank you
try:
with a as (
select
case when fecha_contratado > current_date - interval '100 days' then 1
else 0 end q1
, case when fecha_fin_contrato > current_date - interval '100 days' then 1
else 0 end q2
from rrhh.empleado
)
select sum(q1), sum(q2)
from a
;
This is a typical case for conditional aggregation:
select count(*) filter (where fecha_contratado > current_date - interval '100 days'),
count(*) filter (where fecha_fin_contrato > current_date - interval '100 days')
from rrhh.empleado
You can use the CASE expression (and the fact that most aggregates does not use NULL values) for versions earlier than 9.4:
select count(case when fecha_contratado > current_date - interval '100 days' then 1 end),
count(case when fecha_fin_contrato > current_date - interval '100 days' then 1 end)
from rrhh.empleado
Note: these queries will scan the whole table, while your original queries could make use of indexes on fecha_contratado and fecha_fin_contrato. If performance matters to you, you could append a filter to these queries too:
where least(fecha_contratado, fecha_fin_contrato) > current_date - interval '100 days'
and you could index the expression: least(fecha_contratado, fecha_fin_contrato).

Find new records for each of the past n months in PostgreSQL

I have a table of records with a createddate in them. I would like to return the new records for each of the last 6 calendar months, including the partial this month. I know SQL Server well but am not as familiar with PostgreSQL.
I have been able to get data for rolling months with this query:
select
COUNT(ID) as Total,
COUNT(CASE WHEN createddate between (now() - '1 month'::interval)::timestamp AND now() THEN AG.ID END) as ThisMonth,
COUNT(CASE WHEN createddate between (now() - '2 month'::interval)::timestamp AND (now() - '1 month'::interval)::timestamp THEN AG.ID END) as LastMonth,
COUNT(CASE WHEN createddate between (now() - '3 month'::interval)::timestamp AND (now() - '2 month'::interval)::timestamp THEN AG.ID END) as PrevMonth,
COUNT(CASE WHEN createddate between (now() - '4 month'::interval)::timestamp AND (now() - '3 month'::interval)::timestamp THEN AG.ID END) as PrevMonth2,
COUNT(CASE WHEN createddate between (now() - '5 month'::interval)::timestamp AND (now() - '4 month'::interval)::timestamp THEN AG.ID END) as PrevMonth3,
COUNT(CASE WHEN createddate between (now() - '6 month'::interval)::timestamp AND (now() - '5 month'::interval)::timestamp THEN AG.ID END) as PrevMonth4
FROM a_group AG
But on 6/21, this will return data from 5/22-6/21, 4/22-5/21, etc.
I would like the data to bucket as follows: 6/1-6/21 (partial current month), 5/1-5/31, etc.
Any suggestions? I also suspect I could do this in a loop but am not familiar enough with the syntax yet. For now, I am testing this from PostgreSQL Maestro against a backup file.
Thanks.
I think the date_trunc function may be your friend (see postgres docs). You would do something like this I guess:
select
COUNT(ID) as Total,
COUNT(CASE WHEN createddate between date_trunc('month', now()) AND now() THEN AG.ID END) as ThisMonth,
COUNT(CASE WHEN createddate between date_trunc('month', now()) - interval '1 month' AND date_trunc('month', now()) - interval '1 day' THEN AG.ID END) as LastMonth,
etc...