レコードが存在するかどうかを確認する最速の方法


143

タイトルが示すように...レコードがテーブルに存在するかどうかを判断するために、オーバーヘッドを最小限に抑えて最速の方法を見つけようとしています。

サンプルクエリ:

SELECT COUNT(*) FROM products WHERE products.id = ?;

    vs

SELECT COUNT(products.id) FROM products WHERE products.id = ?;

    vs

SELECT products.id FROM products WHERE products.id = ?;

言う?と交換される'TB100'...第1および第2の両方のクエリは(...と言うまったく同じ結果を返します。1この会話のため)。最後のクエリは'TB100'期待どおりに返すかid、テーブルにない場合は何も返しません。

目的はid、テーブルにあるかどうかを把握することです。そうでない場合、プログラムは次にレコードを挿入します。そうである場合、プログラムはそれをスキップするか、この質問の範囲外の他のプログラムロジックに基づいてUPDATEクエリを実行します。

どちらが速く、オーバーヘッドが少ないですか?(これはプログラムの実行ごとに数万回繰り返され、1日に何度も実行されます)。

(M $提供のJDBCドライバーを介してJavaからM $ SQL Serverに対してこのクエリを実行)


1
これはデータベースに依存する場合があります。たとえば、Postgresを頼りにすると、かなり時間がかかります。
マイククリステンセン

申し訳ありませんが、これはjdbcドライバを介してM $ SQLと通信するJavaです。OPを更新します。
SnakeDoc 2013

2
そこでは存在しても。
NikolaMarkovinović2013

@NikolaMarkovinović:この場合、どのように使用しますか?
zerkms 2013

5
@zerkmsコンテキストによって異なります。ストアドプロシージャの場合はif exists(select null from products where id = @id)、クライアントから直接呼び出されたクエリの場合select case when exists (...) then 1 else 0 end
NikolaMarkovinović2013

回答:


170

SELECT TOP 1 products.id FROM products WHERE products.id = ?; 最初のレコードが見つかると実行が終了するため、すべての提案よりも優れています。


5
PK(またはその他の一意のキー)を検索するときに、オプティマイザーはそれを考慮に入れませんか?
zerkms 2013

3
彼はそれがPKであると述べたが、そうであれば、オプティマイザはそれを考慮に入れるだろうと述べた。
Declan_K 2013

3
@Declan_K:この場合、魔法の球が失敗したようで、列にidPKではないというタイトルが付けられています。だからあなたのアドバイスに+1。
zerkms 2013

4
PKでない場合は、その列にインデックスがあることを確認することもお勧めします。それ以外の場合、クエリはより高速なテーブルシークではなくテーブルスキャンを実行する必要があります。
CDヨルゲンセン2013

3
私はこれよりも@ nenad-zivkovicの答えを検討する必要があると思います。
Giulio Caccin 2015

192

EXISTS(またはNOT EXISTS)は、何かが存在するかどうかをチェックするために特別に設計されているため、最良のオプションである必要があります。一致する最初の行で停止するため、TOP句を必要とせず、実際にはデータを選択しないため、列のサイズにオーバーヘッドがありません。SELECT *ここで安全に使用できます- SELECT 1SELECT NULLまたはSELECT AnyColumn... と同じです(のような無効な式を使用することもでき、SELECT 1/0壊れません)

IF EXISTS (SELECT * FROM Products WHERE id = ?)
BEGIN
--do what you need if exists
END
ELSE
BEGIN
--do what needs to be done if not
END

これは最初にSELECTステートメントを実行し、次にIF EXISTSステートメントを実行する必要はありません...追加のオーバーヘッドを引き起こし、そのため処理時間を長くしますか?
SnakeDoc 2013

7
@SnakeDoc号は、Existsで動作するselectが、すぐに一列が見つかったとして出るような方法で。さらに、レコードの実際の値ではなく、レコードの存在を示すだけで存在するため、ディスクから行をロードする必要がなくなります(もちろん、検索基準に索引が付けられていると想定しています)。if-のオーバーヘッドについては、とにかくこのごくわずかな時間を費やす必要があります。
NikolaMarkovinović2013

1
@NikolaMarkovinović興味深い点。このフィールドにインデックスが存在するかどうかはわかりません。また、新しいSQLで検索する方法がわかりません。私は、JDBCを介してJavaからこのDBを操作しています。データベースは、どこかにあるコロのリモートにあります。各テーブルにどのフィールドが存在するか、そのタイプ、およびFKまたはPKの詳細のみを示す「データベースの概要」のみが提供されています。これは何かを変えますか?
SnakeDoc 2013

3
@SnakeDoc外部キーやインデックスを含むテーブル構造について調べるには、sp_help table_nameを実行します。インデックスは、orを使用するselect top場合にexists、多数の行から数行を取得する場合に不可欠です。それらが存在しない場合、SQLエンジンはテーブルスキャンを実行する必要があります。これは、最も望ましくないテーブル検索オプションです。インデックスを作成する権限がない場合は、反対側の技術スタッフに連絡して、インデックスを自動的に調整するか、インデックスの提案を期待するかを確認する必要があります。
NikolaMarkovinović2013

1
@Konstantinあなたは次のようなことをすることができますSELECT CASE WHEN EXISTS(..) THEN 1 ELSE 0 END;
Nenad

21

何にも勝てない-

SELECT TOP 1 1 FROM products WHERE id = 'some value';

テーブルにデータがあるかどうかを知るために数える必要はありません。また、不要な場合はエイリアスを使用しないでください。


5
その名前にもかかわらず、id主キーではありません。したがって、数えていなくても、一致するすべてのレコード、場合によっては数千件を見つける必要があります。エイリアシングについて-コードは常に進行中です。いつ戻るかわからない。エイリアシングは、愚かな実行時エラーの防止に役立ちます。たとえば、エイリアスを必要としない一意の列名は、誰かが別の結合されたテーブルに同じ名前の列を作成したため、もう一意ではありません。
NikolaMarkovinović2013

はい、あなたは完全に正しいです。エイリアシングは非常に役立ちますが、結合を使用しない場合は違いはないと思います。だから、必要ないのなら使わないように言った。:)そして、あなたは存在の確認についてここで長い議論を見つけることができます。:)
AgentSQL 2013

3
なぜこの用語を受け入れたのかわかりませんaliasing。正しい用語はqualifyingです。ここでアレックスKuznetzovによって長い説明が。単一テーブルクエリについて-単一テーブルになりました。しかし、後でバグが発見され、洪水に耐えようとすると、クライアントは緊張し、エラーメッセージに直面するために別のテーブルに参加します-簡単に修正可能なメッセージですが、この汗ばんだ瞬間ではなく、小さなストロークが発生し、列を離れないことを思い出してエラー...
NikolaMarkovinović2013

1
今はそれを無視することはできません。ありがとう!! :)
AgentSQL 2013

15
SELECT CASE WHEN EXISTS (SELECT TOP 1 *
                         FROM dbo.[YourTable] 
                         WHERE [YourColumn] = [YourValue]) 
            THEN CAST (1 AS BIT) 
            ELSE CAST (0 AS BIT) END

このアプローチはブール値を返します。


1
Existはレコードを見つけると終了するため、Topステートメントと*ステートメントを省略して少し高速にすることができます。そのため、次のようになります。SELECT CASE WHEN EXISTS(SELECT 1 FROM dbo。[YourTable] WHERE [YourColumn] = [YourValue])THEN CAST(1 AS BIT)ELSE CAST(0 AS BIT)END
Stefan Zvonar 2017

この提案では、SQL Server内の組み込みのexists / not existsステートメントよりも高速になる理由については触れていません。ベンチマークがなければ、ケースステートメントが即時の真/偽の応答よりも速い結果をもたらすと信じるのは難しいでしょう。
Bonez024

8

あなたも使うことができます

 If EXISTS (SELECT 1 FROM dbo.T1 WHERE T1.Name='Scot')
    BEGIN
         --<Do something>
    END 

ELSE    
     BEGIN
       --<Do something>
     END

7

まだ誰も言及していないと思いますが、データが自分の下で変更されないことが確かな場合は、NoLockヒントを適用して、読み取り時にブロックされないようにすることもできます。

SELECT CASE WHEN EXISTS (SELECT 1 
                     FROM dbo.[YourTable] WITH (NOLOCK)
                     WHERE [YourColumn] = [YourValue]) 
        THEN CAST (1 AS BIT) 
        ELSE CAST (0 AS BIT) END

3
SELECT COUNT(*) FROM products WHERE products.id = ?;

これは、すべてのデータベースで機能する相互リレーショナルデータベースソリューションです。


6
ただし、大きなテーブルでは速度が非常に遅いため、すべてのレコードをループするようにdbを強制します
amd

@amdは理由を説明しますか?
UmNyobe

@amdコメントは完全に意味があります。このクエリは、FIND ANYではなくFIND ALLです。
UmNyobe

1

以下は、データベースにレコードが存在するかどうかを判断する最も簡単で最速の方法です。良いことは、すべてのリレーショナルDBで機能することです。

SELECT distinct 1 products.id FROM products WHERE products.id = ?;

0
create or replace procedure ex(j in number) as
i number;
begin
select id into i from student where id=j;
if i is not null then
dbms_output.put_line('exists');
end if;
exception
   when no_data_found then
        dbms_output.put_line(i||' does not exists');

end;

2
おそらくコードはうまく機能しますが、理解しやすくなるように情報を追加するとよいでしょう。
idmean 2014年

0

私は過去にこれを使用したことがあり、何かが存在するかどうかを確認するために全表スキャンを行う必要はありません。超高速です...

UPDATE TableName SET column=value WHERE column=value
IF @@ROWCOUNT=0
BEGIN
     --Do work
END             

0

MySQLまたはOracleのバックグラウンドでこれに遭遇した場合-MySQLはLIMIT句をサポートして、限られた数のレコードを選択しますが、OracleはROWNUMを使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.