【问题标题】:aggregate inner query SQL optimization聚合内查询 SQL 优化
【发布时间】:2020-05-13 00:07:20
【问题描述】:

我有 3 张桌子:

create table users
    (
        user_id varchar(50),
        birth_year int,
        country varchar(50)
    )


create table notifications 
    (
        status varchar(50), 
        user_id varchar(50), 
        created_date datetime
    )

create table transactions
    (
        transaction_id varchar(50),
        user_id varchar(50),
        created_date datetime
    )

我想要做的是对于所有收到通知的用户,通知到达前 7 天与通知后 7 天的平均交易量有什么区别 到达的国家和年龄组。

我所做的是:

select q.country
, case when q.age <= 18 then '<= 18'
    when q.age <= 30 then '19 - 30'
    when q.age <= 45 then '31 - 45'
    when q.age <= 60 then '46 - 60'
    else '> 60' end as age_group
, AVG(q.prev_transactions*1.0) as avg_prev_transactions, AVG(q.post_transactions*1.0) as avg_post_transactions
from (
    select n.user_id, n.created_date, u.country, (2019 - u.birth_year) as age
    , count(distinct prev.transaction_id) as prev_transactions, count(distinct post.transaction_id) as post_transactions
    from notifications n
    left outer join transactions post on n.user_id = post.user_id and post.created_date > n.created_date and post.created_date < n.created_date + interval '7' day
    left outer join transactions prev on n.user_id = prev.user_id and prev.created_date < n.created_date and prev.created_date > n.created_date - interval '7' day
    left outer join users u on u.user_id = n.user_id
    where status = 'SENT'
    group by n.user_id, n.created_date, u.country, (2019 - u.birth_year)
    --order by n.user_id asc, n.created_date asc
    ) as q
group by q.country, case when q.age <= 18 then '<= 18'
    when q.age <= 30 then '19 - 30'
    when q.age <= 45 then '31 - 45'
    when q.age <= 60 then '46 - 60'
    else '> 60' end

我想知道是否有办法让它更高效。

谢谢

【问题讨论】:

  • 你为什么要在这里乘以 1:AVG(q.prev_transactions*1.0)?
  • 强制它成为一个浮点数(我来自 Transact-SQL 背景,对 Postgres 没有太多经验)
  • 如果可能,请set track_io_timing=on,然后显示EXPLAIN (ANALYZE, BUFFERS) 进行查询。此外,在内部查询“q”中单独运行,并将其显示出来。

标签: sql postgresql metabase


【解决方案1】:

您在“事务”上的两个左连接可能是个问题。如果有 30 个 prev 事务和 30 个 post 事务,那么这两个连接本质上是笛卡尔连接在一起,创建 900 个 prev-post 配对。然后使用 DISTINCT 将这些值减少回 30。但是您正在做的工作既是创建,然后是删除琐碎的行。

您可以将它们放在每个子选择中,而不是作为连接。

select n.user_id, n.created_date, u.country, (2019 - u.birth_year) as age,
    (select count(*) from transactions post on n.user_id = post.user_id and post.created_date > n.created_date and post.created_date < n.created_date + interval '7' day) as post_transactions,
...

另外,为什么左加入反对用户?对于没有用户的通知,可以获得哪些可能有意义的输出?

【讨论】:

  • 我在一个虚拟数据库中使用了您的建议,与我的初始查询相比,它在查询成本(执行计划)方面显示出巨大的飞跃(23% vs 77%)但它仍然是执行时间过长。
  • 我将用户表上的外连接替换为内连接。
猜你喜欢
  • 1970-01-01
  • 2010-12-18
  • 2021-09-03
  • 2010-10-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多