2

あなたのキャラクターが過去 24 時間以内に挑戦したことのないすべてのキャラクターを選択したいと思います。

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

これは、キャラクターが過去 1 日に開始したチャレンジを含むいくつかの行を返します。

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

WHERE NOT EXISTS を間違って使用していますか?

4

3 に答える 3

3

WHERE NOT EXISTS を間違って使用していますか?

はい。NOT EXISTS ではなく NOT IN を使用する必要があります。NOT EXISTS を使用し、存在しないサブクエリが行を返す場合、条件は false になり、メイン クエリによってデータは返されません。行が返されない場合、条件は true になり、すべての行がメイン クエリによって返されます (この例では、メイン クエリに他の基準がないため)。多くの場合、NOT EXISTS のサブクエリは相関サブクエリであるため、サブクエリは行ごとに評価する必要があります。ここでは、相関サブクエリはありません (パフォーマンスに適しています)。ただし、クエリは、「指定されたユーザーが過去 1 日に挑戦したキャラクターが存在しない限り、すべてのキャラクターに関する情報を返す」ことを意味します。

(この分析では、SQL を静かに変更して、userCharID常に文字列と、'642'具体的には値と比較されるようにしました。 )

あなたのキャラクターが過去 24 時間以内に挑戦したすべてのキャラクターを選択してください:

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

これは、キャラクターが過去 1 日に開始した課題を含むいくつかの行を返します。

したがって、チャレンジしていないすべての人を見つけるには、チャレンジしたリスト内のユーザーを除くすべてのユーザーを選択する必要があります。これは、次のように変換されます。

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

これにより、過去 24 時間以内に挑戦したことのないキャラクターの (おそらくかなり大きい) リストが表示されます。

于 2012-07-03T00:35:31.803 に答える
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))

2 番目characterID(サブクエリ内のもの)が CHARACTERS テーブルの characterID に対応するフィールドである必要がある場合、これは userCharID である可能性がありますが、where 句が与えられているとは思えません。スキーマがないと、確かなことはわかりません。

自由に使用できる他のオプションは、サブクエリから直接選択するか、場合によっては joinを介してデータを取得することです。

于 2012-07-03T00:22:42.313 に答える
0

あなたの 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

于 2012-07-19T16:04:20.850 に答える