2017-10-05 24 views
1

可以想象我有类似以下的查询:如何将多个子查询优化到同一数据集

SELECT 
    u.ID, 
    (SELECT 
     COUNT(*) 
    FROM 
     POSTS p 
    WHERE 
     p.USER_ID = u.ID 
     AND p.TYPE = 1 
) AS interesting_posts, 
    (SELECT 
     COUNT(*) 
    FROM 
     POSTS p 
    WHERE 
     p.USER_ID = u.ID 
     AND p.TYPE = 2 
) AS boring_posts, 
    (SELECT 
     COUNT(*) 
    FROM 
     COMMENTS c 
    WHERE 
     c.USER_ID = u.ID 
     AND c.TYPE = 1 
) AS interesting_comments, 
    (SELECT 
     COUNT(*) 
    FROM 
     COMMENTS c 
    WHERE 
     c.USER_ID = u.ID 
     AND c.TYPE = 2 
) AS boring_comments 
FROM 
    USERS u; 

(希望这是正确的,因为我只是想出了它,并没有测试)

我尝试计算用户拥有的有趣和无聊的帖子和评论的数量。

现在,这个查询的问题是,我们有两个顺序扫描postscomments表,我不知道是否有办法避免这种情况?

我大概可以LEFT JOINusers表的帖子和评论,并做了一些聚合,但它会在聚合之前产生大量的行,我不知道如果这是一个好方法。

+0

您可以使用解释来获得查询成本的近似概念,并使用小的更改进行优化。 –

+1

是的,尝试两个LEFT JOIN +(条件)聚合。 – wildplasser

+0

@wildplasser我使查询有点复杂。我与左连接的问题是,它会在聚合之前生成用户*注释*发布行,这可能是很多行,并且可能会减慢查询速度。 – JustMichael

回答

3

聚合信息和评论外将他们加入到用户表。

select 
    u.id as user_id, 
    coaleasce(p.interesting, 0) as interesting_posts, 
    coaleasce(p.boring, 0)  as boring_posts, 
    coaleasce(c.interesting, 0) as interesting_comments, 
    coaleasce(c.boring, 0)  as boring_comments 
from users u 
left join 
(
    select 
    user_id, 
    count(case when type = 1 then 1 end) as interesting, 
    count(case when type = 2 then 1 end) as boring 
    from posts 
    group by user_id 
) p on p.user_id = u.id 
left join 
(
    select 
    user_id, 
    count(case when type = 1 then 1 end) as interesting, 
    count(case when type = 2 then 1 end) as boring 
    from comments 
    group by user_id 
) c on c.user_id = u.id; 
0

比较结果和执行计划(在这里你扫描的帖子一次):

with c as (
select distinct 
count(1) filter (where TYPE = 1) over (partition by USER_ID) interesting_posts 
, count(1) filter (where TYPE = 2) over (partition by USER_ID) boring_posts 
, USER_ID 
) 
, p as (select USER_ID,max(interesting_posts) interesting_posts, max(boring_posts) boring_posts from c) 
SELECT 
    u.ID, interesting_posts,boring_posts 
    , (SELECT 
     COUNT(*) 
    FROM 
     COMMENTS c 
    WHERE 
     c.USER_ID = u.ID 
) AS comments 
FROM 
    USERS u 
JOIN p on p.USER_ID = u.ID