如何优化SQL查询以汇总一系列日期中各个日期的数据?

时间:2019-05-28 14:25:12

标签: sql postgresql postgresql-9.6

我正在尝试优化下面的SQL查询,该查询返回一系列日期中任何给定日期具有特定状态的所有产品的计数。在任何给定的日期,该产品的最新状态应为查询中给出的状态(在下面的查询摘要中为“ 2”),并且该日期之前该产品不应有任何其他状态更新。该查询返回正确的结果,但是如果特定商店中的产品数量很大并且需要查询30天,则运行时间会更长(〜12-15秒)。

如果查询的天数更多,则查询将花费更长的时间来运行,即查询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 :(得分:0)

  

“在任何一天,产品的最新状态应为   查询中给定的那个(在下面的查询摘录中为“ 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;

我也对statisticdate进行了强制转换,而不是对psh.created_at进行了强制转换,因为这样可以使用created_at上的可能索引。