Как оптимизировать SQL-запрос, который объединяет данные за отдельные дни за серию дат? - PullRequest
0 голосов
/ 28 мая 2019

Я пытаюсь оптимизировать приведенный ниже SQL-запрос, который возвращает количество всех продуктов с определенным статусом в любой день в серии дат. В любой конкретный день последний статус продукта должен соответствовать статусу, указанному в запросе (в приведенном ниже фрагменте запроса это «2»), и до этой даты не должно быть никаких дальнейших обновлений статуса этого продукта. Запрос возвращает правильные результаты, но его выполнение занимает больше времени (~ 12–15 секунд), если количество товаров в конкретном магазине велико и запрашивается в течение 30 дней.

Выполнение запроса занимает больше времени, если количество запрашиваемых дней больше, т. Е. Для запроса в течение 7 дней требуется всего 3 секунды, но мне нужно запрашивать только 30 дней.

select
statisticDate as date,
(
select
    count(*)
from
    product as p
join product_status_history as psh1 on
    p.id = psh1.product_id
    and psh1.id = (
    select
        min(id)
    from
        product_status_history
    where
        product_id = p.id
        and productstatus_id = 2
    group by
        product_id)
join product_status_history as psh2 on
    p.id = psh2.product_id
    and psh2.id = (
    select
        max(id)
    from
        product_status_history
    where
        product_id = p.id
    group by
        product_id)
where
    p.store_code = 'ABCD123'
    and ((psh2.productstatus_id = 2
    and cast(psh2.created_at as date) <= statisticDate)
    or (psh2.productstatus_id <> 2
    and cast(psh1.created_at as date) <= statisticDate
    and cast(psh2.created_at as date) > statisticDate))) as counter
from
    generate_series(current_date - 30, current_date + 1, '1 day') as statisticDate
order by
    statisticDate desc;

Структура двух таблиц выглядит следующим образом

DB structure

И запрос возвращает результаты, подобные этому

Date       - Counter
2019-05-29 - 60
2019-05-28 - 60
2019-05-27 - 111
2019-05-26 - 123
2019-05-25 - 148
2019-05-24 - 234
2019-05-23 - 344
2019-05-22 - 434
2019-05-21 - 339
2019-05-20 - 256
2019-05-19 - 306
2019-05-18 - 392
2019-05-17 - 361
2019-05-16 - 480
2019-05-15 - 406
2019-05-14 - 203
2019-05-13 - 314
2019-05-12 - 396
2019-05-11 - 368
2019-05-10 - 484
2019-05-09 - 420
2019-05-08 - 234
2019-05-07 - 341
2019-05-06 - 204
2019-05-05 - 245
2019-05-04 - 306
2019-05-03 - 408
2019-05-02 - 342
2019-05-01 - 290
2019-04-30 - 272
2019-04-29 - 202
2019-04-28 - 241

1 Ответ

0 голосов
/ 28 мая 2019

"В любой данный день последний статус продукта должен быть тот, который указан в запросе (в приведенном ниже фрагменте запроса это '2') и не должно быть никаких дальнейших обновлений статуса в этом продукте до этой даты. "

Это означает, что для даты X вы хотите рассчитывать только те продукты, которые имеют одну запись истории с created_at < given_date and productstatus_id = 2

Ваш запрос, однако, выполняет другие проверки. Если я учту только ваше описание, вы можете использовать следующий запрос:

count(psh.id)=1 гарантирует наличие только одной истории до даты проверки. max(psh.product_status_id)=2 подтверждает, что эта единственная запись истории имеет правильный статус.

select statisticDate as date,
 (select count(*)
    from product as p
   where p.store_code = 'ABCD123'
     and exists (select max(product_status_id), count(psh.id)
                   from product_status_history psh
                  where psh.product_id = p.id
                    and psh.created_at < cast((statisticDate + interval '1 day') as text):: timestamp
                  having max(psh.product_status_id) = 2 and count(psh.id) = 1)
                )
from generate_series(current_date - 30, current_date + 1, '1 day') as statisticDate
order by statisticDate desc;

Я также произвел приведение к статистической дате вместо psh.created_at, так как можно было использовать возможный индекс для create_at.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...