Как правильно сделать join двух таблиц с одной generate_series по датам?
Закопался в необычный для себя запрос для вывода отчётов о работе системы. Вкратце, есть 2 таблицы:
table1
CREATE TABLE table1
(
id bigint,
eventtime timestamp without time zone,
error integer
waittime interval
worktime interval
)
table2
CREATE TABLE table2
(
id bigint,
eventtime timestamp without time zone,
eventtype integer
timeall interval
timeone interval
)
table1
| id | eventtime | error | waittime | worktime |
|---|---|---|---|---|
| 1 | 2023-05-29 09:11:00 | 0 | 00:00:00.000000 | 00:00:01.000000 |
| 2 | 2023-05-29 09:13:00 | 1 | 00:00:02.000000 | 00:00:00.000000 |
| 3 | 2023-05-29 09:15:00 | 0 | 00:00:00.000000 | 00:00:02.000000 |
| 4 | 2023-05-29 09:16:00 | 1 | 00:00:03.000000 | 00:00:00.000000 |
| 5 | 2023-05-29 09:21:00 | 0 | 00:00:00.000000 | 00:00:03.000000 |
| 6 | 2023-05-29 09:23:00 | 1 | 00:00:02.000000 | 00:00:00.000000 |
table2
| id | eventtime | eventtype | timeall | timeone |
|---|---|---|---|---|
| 1 | 2023-05-29 09:12:00 | 1 | 00:00:01.000000 | 00:00:01.000000 |
| 2 | 2023-05-29 09:14:00 | 2 | 00:00:02.000000 | 00:00:01.000000 |
| 3 | 2023-05-29 09:16:00 | 3 | 00:00:01.000000 | 00:00:02.000000 |
| 4 | 2023-05-29 09:17:00 | 4 | 00:00:03.000000 | 00:00:01.000000 |
| 5 | 2023-05-29 09:21:00 | 1 | 00:00:01.000000 | 00:00:03.000000 |
| 6 | 2023-05-29 09:22:00 | 3 | 00:00:02.000000 | 00:00:01.000000 |
Эти таблицы содержат отметки о разных событиях, которые произошли в определённое время, описан тип события и некое время, на него затраченное тем или иным способом.
Мне требуется составить запрос, с помощью которого я мог бы показать отчёт по этим данным за определённые промежутки времени.
Например, я хочу получить запрос для периода времени от 2023-05-29 09:00:00 до 2023-05-29 09:30:00 с разбивкой на интервалы по 10 минут. В запросе должны быть указаны эти интервалы времени, общее количество записей в соответствующем промежутке времени из первой таблицы, сколько при этом было записей с error != 0, среднее время waittime и среднее worktime для каждого промежутка времени, а также из второй таблицы для каждого промежутка времени рассчитать кол-во записей с eventtype=1, с eventtype=2, среднее время timeall при eventtype=1, рассчитать кол-во записей с eventtype=3, с eventtype=4, среднее время timeone при eventtype=3.
Для этого я составляю следующий запрос:
select gen_series,
count(t1.eventtime) as eventtime1,
count( case
when t1.error!=0 then 1
end) as error1,
COALESCE(cast(avg(t1.waittime ) as varchar), '00:00:00.000000') as waittime,
COALESCE(cast(avg(t1.worktime ) as varchar), '00:00:00.000000') as worktime,
count(case
when t2.eventtype=1 then 1
end) as eventtype1,
count(case
when t2.eventtype=2 then 1
end) as eventtype2,
COALESCE(cast(avg(case
when t2.eventtype=1 then t2.timeall
end)as varchar),'00:00:00.000000') as timeall,
count(case
when t2.eventtype=3 then 1
end) as eventtype3,
count(case
when t2.eventtype=4 then 1
end) as eventtype4,
COALESCE(cast(avg(case
when t2.eventtype=3 then t2.timeone
end)as varchar),'00:00:00.000000') as timeone
from generate_series('2023-05-29 09:00:00', '2023-05-29 09:30:00', interval '10 min') as gen_series
left outer join table1 t1 on (t1.eventtime>=gen_series and t1.eventtime<=gen_series+interval '10 min')
left outer join table2 t2 on (t2.eventtime>=gen_series and t2.eventtime<=gen_series+interval '10 min')
group by gen_series
order by gen_series
Когда я делаю join только с первой таблицей, то всё считается правильно:
| gen_series | eventtime1 | error1 | waittime | worktime |
|---|---|---|---|---|
| 2023-05-29 09:00:00 | 0 | 0 | 00:00:00.000000 | 00:00:00.000000 |
| 2023-05-29 09:10:00 | 4 | 2 | 00:00:02.500000 | 00:00:01.500000 |
| 2023-05-29 09:20:00 | 2 | 1 | 00:00:01.000000 | 00:00:01.500000 |
| 2023-05-29 09:30:00 | 0 | 0 | 00:00:00.000000 | 00:00:00.000000 |
Когда я делаю join только со второй таблицей, то также всё считается правильно:
| gen_series | eventtype1 | eventtype2 | timeall | eventtype3 | eventtype4 | timeone |
|---|---|---|---|---|---|---|
| 2023-05-29 09:00:00 | 0 | 0 | 00:00:00.000000 | 0 | 0 | 00:00:00.000000 |
| 2023-05-29 09:10:00 | 1 | 1 | 00:00:01.000000 | 1 | 1 | 00:00:02.000000 |
| 2023-05-29 09:20:00 | 1 | 0 | 00:00:01.000000 | 1 | 0 | 00:00:01.000000 |
| 2023-05-29 09:30:00 | 0 | 0 | 00:00:00.000000 | 0 | 0 | 00:00:00.000000 |
Но как только я пытаюсь написать их вместе с двумя join, как написано у меня в большом запросе выше, всё работает не совсем как надо.
Половина с table2 считается верно, но в половине с table1 возникает куча дубликатов на каждую запись, выведенную из table2.
Получается что-то в таком духе:
| gen_series | eventtime1 | error1 | waittime | worktime | eventtype1 | eventtype2 | timeall | eventtype3 | eventtype4 | timeone |
|---|---|---|---|---|---|---|---|---|---|---|
| 2023-05-29 09:00:00 | 0 | 0 | 00:00:00.000000 | 00:00:00.000000 | 0 | 0 | 00:00:00.000000 | 0 | 0 | 00:00:00.000000 |
| 2023-05-29 09:10:00 | 16 | 8 | 00:00:02.500000 | 00:00:01.500000 | 1 | 1 | 00:00:01.000000 | 1 | 1 | 00:00:02.000000 |
| 2023-05-29 09:20:00 | 8 | 4 | 00:00:01.000000 | 00:00:01.500000 | 1 | 0 | 00:00:01.000000 | 1 | 0 | 00:00:01.000000 |
| 2023-05-29 09:30:00 | 0 | 0 | 00:00:00.000000 | 00:00:00.000000 | 0 | 0 | 00:00:00.000000 | 0 | 0 | 00:00:00.000000 |
Чувствую, что упускаю какую-то мелочь в запросе, которая на это влияет, но никак не могу понять, какую. Есть какие-нибудь идеи?
Может, есть какой-то иной способ решения или существует какая-нибудь команда типа UNION, но которая просто ставит рядом 2 запроса :D
Ответы (1 шт):
Думаю, в приведенный "большой" запрос невозможно чуть-чуть поправить, чтобы получить желаемый результат.Предлагаю сперва делать генерацию серии и группировку по каждой таблице и уже потом применить JOIN. Желаемый результат не приведен, но попробуйте пример:
with t1 as (
select gen_series,
count(t1.eventtime) as eventtime1,
count( case when t1.error!=0 then 1 end) as error1,
COALESCE(cast(avg(t1.waittime ) as varchar), '00:00:00.000000') as waittime,
COALESCE(cast(avg(t1.worktime ) as varchar), '00:00:00.000000') as worktime
from generate_series('2023-05-29 09:00:00', '2023-05-29 09:30:00', interval '10 min') as gen_series
left outer join table1 t1 on (t1.eventtime>=gen_series and t1.eventtime<=gen_series+interval '10 min')
group by gen_series
)
,t2 as(
select gen_series,
count(case when t2.eventtype=1 then 1 end) as eventtype1,
count(case when t2.eventtype=2 then 1 end) as eventtype2,
COALESCE(cast(avg(case when t2.eventtype=1 then t2.timeall end)as varchar)
,'00:00:00.000000') as timeall,
count(case when t2.eventtype=3 then 1 end) as eventtype3,
count(case when t2.eventtype=4 then 1 end) as eventtype4,
COALESCE(cast(avg(case when t2.eventtype=3 then t2.timeone end)as varchar)
,'00:00:00.000000') as timeone
from generate_series('2023-05-29 09:00:00', '2023-05-29 09:30:00', interval '10 min') as gen_series
left outer join table2 t2 on (t2.eventtime>=gen_series and t2.eventtime<=gen_series+interval '10 min')
group by gen_series
)
select *
from t1 left join t2 on t1.gen_series=t2.gen_series
order by t1.gen_series
Результат запроса (черновик) будет такой
| gen_series | eventtime1 | error1 | waittime | worktime | eventtype1 | eventtype2 | timeall | eventtype3 | eventtype4 | timeone |
|---|---|---|---|---|---|---|---|---|---|---|
| 2023-05-29 09:00:00+01 | 0 | 0 | 00:00:00.000000 | 00:00:00.000000 | 0 | 0 | 00:00:00.000000 | 0 | 0 | 00:00:00.000000 |
| 2023-05-29 09:10:00+01 | 4 | 2 | 00:00:01.25 | 00:00:00.75 | 1 | 1 | 00:00:01 | 1 | 1 | 00:00:02 |
| 2023-05-29 09:20:00+01 | 2 | 1 | 00:00:01 | 00:00:01.5 | 1 | 0 | 00:00:01 | 1 | 0 | 00:00:01 |
| 2023-05-29 09:30:00+01 | 0 | 0 | 00:00:00.000000 | 00:00:00.000000 | 0 | 0 | 00:00:00.000000 | 0 | 0 | 00:00:00.000000 |
В Вашем способе результат всех JOIN (упростим запрос) вызывает размножение строк, как и положено
select gen_series,
t1.id id1,t2.id id2
from generate_series('2023-05-29 09:00:00'
, '2023-05-29 09:30:00', interval '10 min') as gen_series
left outer join table1 t1 on (t1.eventtime>=gen_series and t1.eventtime<=gen_series+interval '10 min')
left outer join table2 t2 on (t2.eventtime>=gen_series and t2.eventtime<=gen_series+interval '10 min')
до группировки получится такой
| gen_series | id1 | id2 |
|---|---|---|
| 2023-05-29 09:00:00+01 | null | null |
| 2023-05-29 09:10:00+01 | 1 | 1 |
| 2023-05-29 09:10:00+01 | 1 | 2 |
| 2023-05-29 09:10:00+01 | 1 | 3 |
| 2023-05-29 09:10:00+01 | 1 | 4 |
| 2023-05-29 09:10:00+01 | 2 | 1 |
| 2023-05-29 09:10:00+01 | 2 | 2 |
| 2023-05-29 09:10:00+01 | 2 | 3 |
| 2023-05-29 09:10:00+01 | 2 | 4 |
| 2023-05-29 09:10:00+01 | 3 | 1 |
| 2023-05-29 09:10:00+01 | 3 | 2 |
| 2023-05-29 09:10:00+01 | 3 | 3 |
| 2023-05-29 09:10:00+01 | 3 | 4 |
| 2023-05-29 09:10:00+01 | 4 | 1 |
| 2023-05-29 09:10:00+01 | 4 | 2 |
| 2023-05-29 09:10:00+01 | 4 | 3 |
| 2023-05-29 09:10:00+01 | 4 | 4 |
| 2023-05-29 09:20:00+01 | 5 | 5 |
| 2023-05-29 09:20:00+01 | 5 | 6 |
| 2023-05-29 09:20:00+01 | 6 | 5 |
| 2023-05-29 09:20:00+01 | 6 | 6 |
| 2023-05-29 09:30:00+01 | null | null |