2017-06-16 89 views
2

我有兩個表 - 國家(id,name)和用戶(id,name,country_id)。每個用戶都屬於一個國家。我想從同一個隨機國家中選擇10個隨機用戶。但是,有些用戶少於10個的國家/地區,因此我無法使用它們。我只需要選擇那些至少有10個用戶的國家。Oracle - 優化SQL查詢

我可以寫這樣的事:

SELECT * FROM(
    SELECT * 
    FROM users u 
    {MANY_OTHER_JOINS_AND_CONDITIONS} 
    WHERE u.country_id = 
    (
    SELECT * 
    FROM 
    (
     SELECT c.id 
     FROM countries c 
     JOIN 
     (
     SELECT users.country_id, COUNT(*) as cnt 
     FROM users 
     {MANY_OTHER_JOINS_AND_CONDITIONS} 
     GROUP BY users.country_id 
    ) X ON X.country_id = c.id 
     WHERE X.cnt >= 10 
     ORDER BY DBMS_RANDOM.RANDOM 
    ) Y 
    WHERE ROWNUM = 1 
) 
    ORDER BY DBMS_RANDOM.RANDOM 
) Z WHERE ROWNUM < 10 

然而,在我的實際情況,我有更多的條件,並加入到其他表以確定哪些用戶是適用的。通過使用這個查詢,我必須在兩個地方有這些條件 - 在查詢中實際選擇數據並在count子查詢中。

有沒有辦法如何寫這樣的查詢,但沒有在兩個地方的其他條件(這可能不是很好的性能方面)?

+0

你可以爲'{MANY_OTHER_JOINS_AND_CONDITIONS}'創建一個視圖... –

+0

嗨,我認爲在這裏優化的最佳方式是使用正確的連接,有時嵌套查詢代價高昂。而不是嵌套查詢使用左或內部聯接取決於需要。 –

+0

@UsagiMiyamoto爲了任何人長期維護這一點 - 不要在視圖中隱藏複雜性 – Palcente

回答

5

您可以使用CTE作爲用戶標準,以避免重複邏輯並允許DB將該設置緩存一次(儘管根據我的經驗,數據庫並不像應該那樣好,因此請檢查您的執行計劃)。

我更比甲骨文在SQL Server的傢伙,和語法是微妙的不同所以這可能需要一些調整還沒有,但嘗試這個辦法:通過爲每個中間除去嵌套和引進名

WITH SafeUsers (ID, Name, country_id) As 
(
    --criteria for users only has to specified here 
    SELECT ID, Name, country_id 
    FROM users 
    WHERE ... 
), 
RandomCountry (ID) As 
( 
    SELECT ID 
    FROM (
     SELECT u.country_id AS ID 
     FROM SafeUsers u -- but we reference it HERE 
     GROUP BY u.country_id 
     HAVING COUNT(u.Id) >= 10 
     ORDER BY DBMS_RANDOM.RANDOM 
    ) c 
    WHERE ROWNUM = 1 
) 
SELECT u.* 
FROM (
    SELECT s.*   
    FROM SafeUsers s -- and HERE 
    INNER JOIN RandomCountry r ON s.country_id = r.ID 
    ORDER BY DBMS_RANDOM.RANDOM 
) u 
WHERE ROWNUM <= 10 

而且一步,這個查詢是突然更容易閱讀和維護。

+0

謝謝,這工作像一個魅力。更可讀,更快,並返回正確的結果。感謝我向CTE介紹我,我從未接觸過這個。我已經喜歡它了:) –

0

你可以創建一個視圖 爲

create view user_with_many_cond as 
    SELECT * 
    FROM users u 
    {MANY_OTHER_JOINS_AND_CONDITIONS} 

部份尋找到您的查詢 你可以使用具有代替查詢外的地方
的順序似乎可以放置在內部查詢內部
所以過濾器爲一排

SELECT * FROM(
    SELECT * 
    FROM user_with_many_cond u 
    WHERE u.country_id = 
    (
     SELECT c.id 
     FROM countries c 
     JOIN 
     (
      SELECT users.country_id, COUNT(*) as cnt 
      FROM user_with_many_cond 
      GROUP BY users.country_id 
      HAVING cnt >=10 
      ORDER BY DBMS_RANDOM.RANDOM 
     ) X ON X.country_id = c.id 
     WHERE ROWNUM = 1 
    ) 
    ORDER BY DBMS_RANDOM.RANDOM 
) Z WHERE ROWNUM < 10 
0

要獲得超過10個用戶的國家:

SELECT users.country_id 
      , row_number() over (order by dbms_random.value()) as rn 
    FROM users 
    GROUP BY users.country_id having count(*) > 10 

用這個作爲一個子查詢,選擇一個國家和抓住一些用戶:

with ctry as (
    SELECT users.country_id 
      , row_number() over (order by dbms_random.value()) as ctry_rn 
    FROM users 
    GROUP BY users.country_id having count(*) > 10 
) 
    , usr as (
     select user_id 
      , row_number() over (order by dbms_random.value()) as usr_rn 
     from ctry 
     join users 
      on users.country_id = ctry.country_id 
     where ctry.ctry_rn = 1 
) 
select users.* 
from usr 
    join users 
    on users.user_id = usr.user_id 
where usr.usr_rn <= 10 
/ 

這個例子忽略了您的{MANY_OTHER_JOINS_AND_CONDITIONS}:請注入他們回來,你需要他們。