あなたの NOT EXIST クエリは本当に近いです。欠けているのは、サブクエリと characterID の外部クエリの間の相関関係だけです。
c
外部クエリのテーブルにエイリアスをd
追加し、サブクエリのテーブルにエイリアスを追加し、サブクエリのWHERE句に1つの述語を追加しました
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.characterID
サブクエリのテーブルc.characterID
から) と (外部クエリのテーブルから) の相関マッチングです。
そのため、クエリは、ユーザーが過去 24 時間にそのユーザーとチャレンジしたかどうか、その外側のテーブルの各文字をチェックしています。したがって、このクエリは指定した結果セットを返します。
しかし...比較的大きな文字セットがあり、チャレンジされた文字セットが比較的小さい場合、これは結果セットを返す最速のクエリではない可能性があります。
結果セットを取得するもう 1 つの方法は、IS NULL 述語で 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
チャレンジされたすべての 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
一致が見つかったかどうかに関係なく、characters テーブルからすべての行が必要なため、操作を使用します。
WHERE 句は、一致が見つかったすべての行を破棄するため、残っているのはチャレンジされていない文字のセットです。
大規模なセットでの私のテストでは、これは通常 aNOT EXISTS
および aよりも優れていNOT IN
ます (適切なインデックスが利用可能な場合)。しかし、時には a のNOT IN
方が速いこともあれば、の方が速いこともNOT EXISTS
あります。
3 つのアプローチすべてを「ポケットに」入れておき、最も適切な方を使用するのが良いと思います。私は通常、anti-join パターン (これは私が書き慣れているものです) から始めて、パフォーマンスを比較するためにNOT EXISTS
と の両方をテストします。NOT IN