215

2 つの postgresql テーブルがあります。

table name     column names
-----------    ------------------------
login_log      ip | etc.
ip_location    ip | location | hostname | etc.

login_logに行がないすべての IP アドレスを取得したいip_location
このクエリを試しましたが、構文エラーがスローされます。

SELECT login_log.ip 
FROM login_log 
WHERE NOT EXIST (SELECT ip_location.ip
                 FROM ip_location
                 WHERE login_log.ip = ip_location.ip)
ERROR: syntax error at or near "SELECT"
LINE 3: WHERE NOT EXIST (SELECT ip_location.ip`

また、このクエリ (機能するように調整したもの) が、この目的に最適なクエリであるかどうかも疑問に思っています。

4

4 に答える 4

498

このタスクには基本的に 4 つの手法があり、すべて標準 SQL です。

NOT EXISTS

多くの場合、Postgres で最速です。

SELECT ip 
FROM   login_log l 
WHERE  NOT EXISTS (
   SELECT  -- SELECT list mostly irrelevant; can just be empty in Postgres
   FROM   ip_location
   WHERE  ip = l.ip
   );

次の点も考慮してください。

LEFT JOIN / IS NULL

場合によっては、これが最速です。多くの場合、最短です。多くの場合、 と同じクエリ プランになりますNOT EXISTS

SELECT l.ip 
FROM   login_log l 
LEFT   JOIN ip_location i USING (ip)  -- short for: ON i.ip = l.ip
WHERE  i.ip IS NULL;

EXCEPT

短い。より複雑なクエリに統合するのは簡単ではありません。

SELECT ip 
FROM   login_log

EXCEPT ALL  -- "ALL" keeps duplicates and makes it faster
SELECT ip
FROM   ip_location;

(ドキュメントごとに)注意してください:

を使用しない限り、重複は削除EXCEPT ALLされます。

通常、ALLキーワードが必要です。気にしない場合でも、クエリが高速になるため、引き続き使用してください。

NOT IN

値がない場合、または適切NULLに処理することを知っている場合にのみ有効です。この目的には使用しません。また、テーブルが大きくなると、パフォーマンスが低下する可能性があります。NULL

SELECT ip 
FROM   login_log
WHERE  ip NOT IN (
   SELECT DISTINCT ip  -- DISTINCT is optional
   FROM   ip_location
   );

NOT INNULL両側の値の「トラップ」を運ぶ:

MySQL を対象とした dba.SE に関する同様の質問:

于 2013-10-14T16:22:17.437 に答える