2012-07-03 24 views
2

我想選擇您的角色在過去24小時內未遇到過的所有角色。不存在的SQL子查詢

SELECT * FROM challenges 
WHERE userCharID = 642 AND chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY) 

這我使用WHERE NOT EXISTS錯誤返回你的性格,在過去的一天

SELECT characterID FROM CHARACTERS 
WHERE NOT EXISTS (SELECT * FROM challenges 
        WHERE userCharID = '610' 
        AND chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY)) 

發起挑戰幾行?

+0

爲什麼'userCharID'在一個查詢中引用的比較值而不是另一個查詢中的值;爲什麼它在一個查詢中是642,而在另一個查詢中是610?這很重要嗎? –

回答

3

我使用WHERE NOT EXISTS錯誤嗎?

是的。你想要使用NOT IN而不是NOT EXISTS。如果使用NOT EXISTS並且非存在子查詢返回任何行,則條件將爲false,並且主查詢不會返回任何數據。如果沒有行被返回,那麼條件將爲true,所有行將由主查詢返回(因爲在此示例中,主查詢中沒有其他條件)。通常,NOT EXISTS中的子查詢是一個相關的子查詢,因此必須爲每一行評估子查詢。在這裏,你沒有相關的子查詢(這對性能有好處)。但是你的查詢意味着'返回關於所有角色的信息,除非存在被指定用戶在最後一天挑戰過的某些角色'。

)在此分析中,我已悄悄地更改了SQL,以便userCharID總是與字符串進行比較,並且具體使用值'642'

選擇你的性格[]挑戰在過去24小時內的所有字符:

SELECT * 
    FROM Challenges 
WHERE userCharID = '642' 
    AND chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY) 

這將返回與你的性格,在過去的一天已經開始挑戰了幾行。

因此,要找到所有你沒有挑戰的人,你需要選擇所有用戶,除了那些在列表中你有挑戰,轉化爲:

SELECT characterID 
    FROM Characters 
WHERE userCharID NOT IN 
     (SELECT userCharID 
      FROM Challenges 
     WHERE userCharID = '642' 
      AND chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY) 
     ) 

這應該給你在過去的24小時內你還沒有挑戰的角色列表(可能相當大)。

+0

令人敬畏的答案,任何方式來優化?它似乎不是一個非常昂貴的操作。 –

+0

NOT IN可能是一項昂貴的操作。您正在使用MySQL,MySQL的優化超出了我的主要專業領域。如果它是一個IN操作並且代價很高,那麼你可以用JOIN替換它。然而,NOT IN也是'!= ALL'(也就是說,你可以編寫'userCharID!= ALL(SELECT ...)',這就提示了爲什麼它很昂貴;它必須比較每一個如果優化器不確保對'NOT IN列表'的訪問進行了適當的優化,它將會抓取。 –

+0

請注意,如果列表足夠小,則線性搜索可能是最優的,如果挑戰字符的列表較大,那麼某種散列或索引(二叉樹或簡單排序的數組和二進制搜索)搜索可能是合適的,但優化程序是否意識到了這一點。在每次檢查一個字符時都不會重新建立列表;這在這個例子中是不好的(這是正常的,如果它是一個相關的子查詢;這不是一個相關的子查詢,它不應該被處理) –

2

WHERE NOT EXISTS在子查詢的上下文中根據結果返回TRUE或FALSE。

如果子查詢返回任何行,則EXISTS子查詢爲TRUE,NOT EXISTS子查詢爲FALSE。

你的情況

這意味着如果

(SELECT * FROM challenges 
WHERE userCharID = '610' AND chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY)) 

返回任何行,在所有然後

查詢西港島線作爲

SELECT characterID FROM CHARACTERS WHERE FALSE; 

進行評估,這顯然不是你想要的。

可以使用IN操盤手:

SELECT characterID FROM CHARACTERS 
WHERE characterID NOT IN (SELECT characterID FROM challenges 
WHERE userCharID = '610' AND chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY)) 

當第二characterID(一子查詢)必須對應於或CharacterId在你的人物表領域,這可能是userCharID給你,雖然我懷疑它,給你的where子句。沒有這個模式,我無法確定。

其他選項可供您選擇直接from the subquery或在某些情況下通過joins獲取您的數據。

+0

感謝您的回答,我嘗試過這個開發工作,但它在生產中減慢了爬行速度。有沒有什麼辦法來優化這個查詢? –

+0

取決於您的設置。你使用任何索引?最好查看一下查詢分析器以查看處理它的內部查詢的類型。我們無法建議您訪問與您相同的數據庫,或至少使用表+索引結構和數據庫使用的查詢路徑。使用mysql,你可以通過在['EXPLAIN'](http://dev.mysql.com/doc/refman/5.0/en/explain.html)之前在你的sql語句之前得到這個信息,即使那時數據庫優化是它自己的領域,每臺服務器都需要其特定設置。 –

0

您的NOT EXIST查詢非常接近。所有缺少的是子查詢和characterID上的外部查詢之間的關聯。

我剛加入的別名c表上的外部查詢,別名d在你的子查詢表,並在子查詢中添加一個謂詞WHERE子句

SELECT characterID FROM CHARACTERS c 
WHERE NOT EXISTS (SELECT * FROM challenges d 
        WHERE d.userCharID = '610' 
        AND d.chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY) 
        AND d.characterID = c.characterID)  

的「絕招」這裏是(在子查詢的表)的相關匹配d.characterIDc.characterID(外部查詢從表中。)

因此,查詢被檢查在外部表中的每個字符,無論是我們的用戶在過去24小時內對用戶有的挑戰。所以,這個查詢將返回你指定的結果集。

但是...如果你有一個相對較大的字符集合,並且有一個相對較小的集合被挑戰,這可能不會成爲返回結果集的最快查詢。


來獲得結果集的另一種方法是使用LEFT JOIN與IS NULL謂詞如果該查詢(我們稱之爲「反連接」。):

SELECT d.characterID 
    FROM challenges d 
WHERE d.userCharID = 642 
    AND d.chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY) 
GROUP BY d.characterID 

返回已挑戰所有或CharacterId,這是您要在集中的所有字符的排除字符集的列表,那麼你可以使用該查詢作爲內嵌視圖,就像這樣:

SELECT n.characterID 
    FROM characters n 
    LEFT 
    JOIN (
     SELECT d.characterID 
      FROM challenges d 
      WHERE d.userCharID = 642 
      AND d.chalTime > DATE_SUB(CURDATE(), INTERVAL 1 DAY) 
      GROUP BY d.characterID 
     ) c 
    ON c.characterID = n.characterID 
WHERE c.characterID IS NULL 

這裏 我們得到所有字符(n)的列表,並將它們匹配到已被挑戰的字符列表(子查詢別名爲c)。我們使用LEFT JOIN操作,因爲我們希望字符表中的所有行,無論是否找到匹配。

WHERE子句然後拋出所有我們確實找到匹配的行,所以我們留下的是沒有被挑戰的一組字符。


在我與大型成套測試,這通常會優於一個NOT EXISTSNOT IN(在適當的索引可用)。但有時我發現NOT IN更快,有時NOT EXISTS更快。

我覺得把所有三種方法都放在「口袋裏」是很好的,並且使用最合適的方法。我通常從反連接模式開始(這是我習慣寫的),然後測試NOT EXISTSNOT IN以比較性能。