2017-06-21 98 views
1

我正在使用MySQL,我有一個查詢。還有一個子查詢。MySQL和使子查詢更高效

SELECT * FROM rg, list, status 
WHERE (
    (rg.required_status_id IS NULL AND rg.incorrect_status_id IS NULL) || 
    (status.season_id = rg.required_status_id AND status.user_id = list.user_id) || 

    (rg.incorrect_status_id IS NOT NULL AND 
    list.user_id NOT IN (SELECT user_id FROM status WHERE user_id = list.user_id AND season_id = rg.incorrect_status_id) 
) 
) 

的問題是代碼的以下部分:

(rg.incorrect_status_id IS NOT NULL AND 
    list.user_id NOT IN (SELECT user_id FROM status WHERE user_id = list.user_id AND season_id = rg.incorrect_status_id) 
) 

我怎麼能檢查表「身份」具有一排,其中user_id是一樣list.user_idseason_id是一樣rg.incorrect_status_id

更新

這裏是我當前的代碼,但它不會在所有的工作。我不知道該怎麼辦。

SELECT * FROM rg, list, status 
    LEFT JOIN status AS stat 
    INNER JOIN rg AS rglist 
    ON rglist.incorrect_status_id = stat.season_id 
    ON stat.season_id = rglist.incorrect_status_id 

    WHERE (
    (rg.required_status_id IS NULL AND rg.incorrect_status_id IS NULL) || 
    (status.season_id = rg.required_status_id AND status.user_id = list.user_id) || 

    (rg.incorrect_status_id IS NOT NULL AND stat.user_id IS NULL) 
) 
) 

更新2

我修改了名字,但基本思路是一樣的。

FROM sarjojen_rglistat, sarjojen_rglistojen_osakilpailut, kilpailukausien_kilpailut, sarjojen_osakilpailuiden_rgpisteet 
, sarjojen_kilpailukaudet, sarjojen_kilpailukausien_kilpailusysteemit 
/* , kayttajien_ilmoittautumiset */ 
/* , sarjojen_kilpailukausien_pelaajastatukset */ 

LEFT OUTER JOIN sarjojen_kilpailukausien_pelaajastatukset 
ON sarjojen_kilpailukausien_pelaajastatukset.sarjan_kilpailukausi_id = sarjojen_rglistat.vaadittu_pelaajastatus_id 

LEFT OUTER JOIN kayttajien_ilmoittautumiset 
ON kayttajien_ilmoittautumiset.kayttaja_id = sarjojen_kilpailukausien_pelaajastatukset.kayttaja_id 

現在,這個說:

柱未發現:1054未知列在「sarjojen_rglistat.vaadittu_pelaajastatus_id '的條款'

爲什麼會這樣呢?

我有一個名爲「sarjojen_rglistat」的表,並有一列「vaadittu_pelaajastatus_id」。

+0

MySQL並沒有很好地優化'OR'。試着將你的查詢分解爲每個條件的單獨查詢,並將它們與'UNION'結合起來。 – Barmar

+0

你知道在第一種情況下,它會用'NULL'連接每行'list'和'status'行的完全交叉乘積的行,對吧? – Barmar

+0

請參閱http://stackoverflow.com/questions/21633115/return-row-only-if-value-doesnt-exist?lq=1,以獲得更好的方式來編寫第三個查詢,查找不匹配另一個行的行表。 – Barmar

回答

1

1)查詢引擎更簡單的查詢更容易解釋和生成有效的計劃。

如果您仔細關注查詢的以下部分,您可能會意識到有點「奇怪」。這是一個線索,方法可能有點太複雜。

...(
list.user_id NOT IN (
    SELECT user_id 
    FROM status 
      /* Note the sub-query cannot ever return a user_id different 
       to the one checked with "NOT IN" above */ 
    WHERE user_id = list.user_id 
     AND season_id = rg.incorrect_status_id) 
) 

查詢過濾其中list.user_id不是一個結果集,不能包含USER_ID的以外list.user_id。當然子查詢可以返回零結果。所以基本上歸結爲一個簡單的存在檢查。

所以一開始,你應該寫寧:

...(
NOT EXISTS (
    SELECT * 
    FROM status 
    WHERE user_id = list.user_id 
     AND season_id = rg.incorrect_status_id) 
) 

2)要清楚你的「什麼連接表」(此指回1爲好)。

你的查詢從3個表中選擇不指定任何連接條件:

FROM rg, list, status 

這將導致一個交叉聯接產生的結果集是所有可能的行匹配的排列組合。如果您的WHERE子句很簡單,那麼查詢引擎可能會隱式地將某些過濾條件提升爲連接條件,但事實並非如此。所以,即使例如,你在每個表中一個非常小的行數:

 
status 20 
rg  100 
list  1000 

Your intermediate result set (before WHERE is applied), 
would need 1000 * 100 * 20 = 2000000 rows! 

它幫助很大,以明確與加盟條件如何各表的行旨在匹配。它不僅使查詢更易於閱讀和理解,而且還有助於避免忽略可能成爲性能考慮的禍害的連接條件。

請注意,在指定連接條件時,某些行可能沒有匹配,這是知道和理解不同類型的連接非常重要的地方。特別是對於你的情況,WHERE子句中的大部分複雜性似乎來自於在行/不匹配時嘗試解析。有關一些有用的信息,請參閱this answer

您的FROM/WHERE子句應該看起來更像下面的樣子。 (很難確定,因爲你沒有說你的表之間的關係或預期的查詢的輸入/輸出,但它應該設置你在正確的軌道上。)

FROM rg 
     /* Assumes rg rows form the base of the query, and not to have 
      some rg rows excluded due to non-matches in list or status. */ 
     LEFT OUTER JOIN status ON 
      status.season_id = rg.required_status_id 
     LEFT OUTER JOIN list ON 
      status.user_id = list.user_id 
WHERE rg.incorrect_status_id IS NULL 
    /* As Barmar commented, it may also be useful to break this 
     OR condition out as a separate query UNION to the above. */ 
    OR (
      rg.incorrect_status_id IS NOT NULL 
     AND NOT EXISTS (
      SELECT * 
      FROM status 
      WHERE user_id = list.user_id 
       AND season_id = rg.incorrect_status_id) 
     ) 

注意,該查詢是很清晰區分表之間的連接方式以及用於過濾連接結果集的內容。

3)最後,非常重要的是,即使最好的查詢沒有正確的索引也沒有什麼好處!

具有錯誤索引的好查詢(或者相反,具有良好索引的錯誤查詢)將會導致效率低下。計算機速度足夠快,您可能不會注意到小型數據庫,但是您可以嘗試使用候選索引來找到數據和工作負載的最佳組合。

在上面的查詢中,您可能需要下列索引。 (有些人可能已經通過主鍵約束範圍之內。)

status.season_id 
status.user_id 
list.user_id 
rg.required_status_id 
rg.incorrect_status_id 
+0

未知列錯誤? 'rg.required_status_id' – xms

+0

@xms這是你的表,你應該知道它是否有'required_status_id'列。當然,你在自己的問題中經常使用它。如果表中有列,我只能猜測你粘貼了錯誤的東西,導致另一個表被別名爲'rg'。 –

+0

請看看我的開場白。有更新2. – xms

1

使用該句柄3例,你用OR結合子查詢的UNION。然後,您可以在每個子查詢中使用明確的JOIN來明確表格是如何相互關聯的(或者在進行完整的交叉產品時完全沒有關係,如rg.required_status_id IS NULL AND rg.incorrect_status_id IS NULL)。

SELECT rg.*, list.*, status.* 
FROM rg 
CROSS JOIN list 
CROSS JOIN status 
WHERE rg.required_status_id IS NULL AND rg.incorrect_status_id IS NULL 

UNION ALL 

SELECT rg.*, list.*, status.* 
FROM rg 
JOIN status ON rg.required_status_id = status.season_id 
JOIN list ON status.user_id = list.user_id 

UNION ALL 

SELECT rg.*, list.*, status.* 
FROM rg 
CROSS JOIN list 
LEFT JOIN status ON status.user_id = list.user_id AND status.season_id = rg.required_status_id 
WHERE rg.incorrect_status_id IS NOT NULL AND status.season_id IS NULL 
+0

+1唯一需要注意的是,如果聯合查詢是互斥的,那麼'UNION ALL'將防止無效嘗試刪除不存在的重複項。 –

+0

好點。我不確定它們是否是這樣,但我認爲是因爲它們會出現空場的情況。 – Barmar

+0

@Barmar感謝您的代碼!這是否會在兩種情況下返回一行:1)如果'status.season_id = rg.required_status_id'或2)'status.season_id = rg.incorrect_status_id'不存在? – xms