2017-10-05 23 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