SUMMARY: I have two data tables: table1 and table2. I want to join them in the following way:
- a unique id value that is the same within both tables
- ALSO, there is a time value in both tables that I need to be within a certain vicinity of one another (e.g. 30 seconds)
- However, the data in both tables is within YYYY-MM-DD HH-MM-SS form
I have tried to do the following:
SELECT * FROM table1 AS t1
LEFT OUTER JOIN table2 t2
ON t1.id = t2.id
AND t2.message_time BETWEEN t1.time + INTERVAL '20 seconds' AND t1.time - INTERVAL '20 seconds'
but it keeps printing out the same row over and over again when I am looping through the result in psycopg2. Is there a better way to do this? Is the query referring to the respective t1.properly?
You need to switch the BETWEEN parameters:
BETWEEN smaller_value AND greater_value
So your join condition needs to be:
ON t1.id = t2.id
AND t2.message_time BETWEEN t1.message_time - interval '20 seconds' AND t1.message_time + interval '20 seconds'
demo:db<>fiddle
Related
I have 2 queries which i need to merge into 1, they are getting data from the same tables but difference is minor between them
I need to merge this queries:
select sum(balance_delta) as "Not Rental"
from h_driver_balance
where
changed between (select current_date - interval '1 days') and (select current_date)
and driver_balance_id in (select id
from driver
where driver_ds_account_id = 16
and callsign not like '0%')
and comment like 'Refill on the terminal 1%';
and
select sum(balance_delta) as "Rental"
from h_driver_balance
where
changed between (select current_date - interval '1 days') and (select current_date)
and driver_balance_id in (select id
from driver
where driver_ds_account_id = 16
and callsign like '0%')
and comment like 'Refill on the terminal 1%';
I've tried to do WITH and UNION, but I seem not to understand something.
If you want two columns, you can do that with a single query and conditional aggregation:
select sum(b.balance_delta) filter (where d.callsign not like '0%') as "Not Rental",
sum(b.balance_delta) filter (where d.callsign like '0%') as "Rental"
from h_driver_balance b
join driver d on d.id = b.driver_balance_id
where b.changed between current_date - interval '1 days' and current_date
and d.driver_ds_account_id = 16
and b.comment like 'Refill on the terminal 1%';
I want my query to have a dynamic date. The way it is written now, I would have to manually change the date every time. Please see the following as an example:
(select*
from table2
where table2.begin_timestamp::date = '2015-04-01')as start
left outer join
(Select *
from table 1
where opened_at::date >= ('2015-04-01' - 15)
and opened_at::date <= '2015-04-01’)
I don't want '2015-04-01' to be hard-coded. I want to run this query over and over for a series of dates.
Using normal joins, you can do this in an on clause or where clause but not inside the subquery. That leads to logic like this:
from (select*
from table2
) start left outer join
table 1
on opened_at::date >= table2.begin_timestamp::date - interval '15 day' and
opened_at::date <= table2.begin_timestamp::date
I'm not a postgres developer but I think you can adapt a technique from the sql server world called "tally tables".
Esentially your goal is to join day d and the window of days that are at most 15 days greater than it.
You can use something like
SELECT * FROM generate_series('2015-04-01'::timestamp,
'2015-04-30 00:00', '1 days');
To generate a date sequence and from there you can write something like
select *
from table a
join generate_series('2015-04-01'::timestamp,'2015-04-30','1 days') s(o)
on a.begin_timestamp::date = s.o
join table2 b
on a.opened_at>= b.begin_timestamp::date - interval '15 days'
and opened_at::date <= table2.begintimestamp::date
Essentially, instead of looping you use a series of the dates between the beginning of the interval and the end of the range to produce the results you are after.
I have a very simpl postgres (9.3) query that looks like this:
SELECT a.date, b.status
FROM sis.table_a a
JOIN sis.table_b b ON a.thing_id = b.thing_id
WHERE EXTRACT(MONTH FROM a.date) = 06
AND EXTRACT(YEAR FROM a.date) = 2015
Some days of the month of June do not exist in table_a and thus are obviously not joined to table_b. What is the best way to create records for these not represented days and assign a placeholder (e.g. 'EMPTY') to their 'status' column? Is this even possible to do using pure SQL?
Basically, you need LEFT JOIN and it looks like you also need generate_series() to provide the full set of days:
SELECT d.date
, a.date IS NOT NULL AS a_exists
, COALESCE(b.status, 'status_missing') AS status
FROM (
SELECT date::date
FROM generate_series('2015-06-01'::date
, '2015-06-30'::date
, interval '1 day') date
) d
LEFT JOIN sis.table_a a USING (date)
LEFT JOIN sis.table_b b USING (thing_id)
ORDER BY 1;
Use sargable WHERE conditions. What you had cannot use a plain index on date and has to default to a much more expensive sequential scan. (There are no more WHERE conditions in my final query.)
Aside: don't use the basic type name (and reserved word in standard SQL) date as identifier.
Related (2nd chapter):
PostgreSQL: running count of rows for a query 'by minute'
I have a table which contains a timezone offset, +1, -7, +5 etc.
I have another, joinable, table which contains logging information including a datetime.
I would like to select out the datetime from the second table with the offset added on to it using for example, INTERVAL '1 hours', INTERVAL '-7 hours' etc.
My psuedocode would be something like:
SELECT l.status, l.inserted + INTERVAL (coalesce(timezone_offset, '0')||' hours' ) AS inserted FROM log l
LEFT OUTER JOIN users u on u.usersid = l.usersid
LEFT OUTER JOIN companies c on c.companiesid = u.companiesid
WHERE l.usersid=?
This doesn't work, but I can't figure out how to make it work in PostgreSQL 8.3.
Any ideas?
It turns out you can multiply an INTERVAL by a number. So if I create an INTERVAL of '1 hours' and multiply that by the stored offset, that will work, yielding:
SELECT l.status, l.inserted + (INTERVAL '1 hours' * coalesce(timezone_offset, '0')) FROM log as l
LEFT OUTER JOIN users u on u.usersid = l.usersid
LEFT OUTER JOIN companies c on c.companiesid = u.companiesid
WHERE l.usersid=?
et voila!
You can also use concatenation, so (l.inserted || ' hours')::INTERVAL will also work.
I need to add minutes coming from an integer column with a timestamp to compare to another column.
Here's an example:
SELECT t1.id_liame, t1.id_table, t1.periodicidade , t3.data_extracao,
CASE WHEN(NOW() < (e.data_extracao + INTERVAL t1.periodicidade || '
MINUTES'))
THEN 'yes' ELSE 'no' END
FROM table1 as t1 LEFT JOIN liame_has_extracao as t2 USING(id_liame)
LEFT JOIN extracao as t3 USING(id_extracao)
l.periodicidade is integer (minutes)
I need to verify if data_extracao(timestamp) is greater then NOW() + l.periodicidade(integer - minutes).
How can i do it?
You can write your query like this:
SELECT
t1.id_liame,
t1.id_table,
t1.periodicidade,
t3.data_extracao,
CASE
WHEN(NOW() < (t3.data_extracao + (INTERVAL '1 min' * t1.periodicidade)))
THEN 'yes' ELSE 'no'
END
FROM table1 AS t1
LEFT JOIN liame_has_extracao AS t2 USING(id_liame)
LEFT JOIN extracao AS t3 USING(id_extracao)
As you can see, you can multiply intervals with integers so with INTERVAL '1 minute' you define your base unit and multiply your actual time interval.
Hope that helps