SQL:レコードが存在するかどうかを適切にチェックする方法


207

SQLチューニング関連のドキュメントを読んでいると、次のことがわかりました。

SELECT COUNT(*)

  • 行数をカウントします。
  • 多くの場合、レコードの存在を確認するために不適切に使用されます。

あるSELECT COUNT(*)本当に悪いですか?

レコードの存在を確認する適切な方法は何ですか?

回答:


252

次のいずれかを使用することをお勧めします。

-- Method 1.
SELECT 1
FROM table_name
WHERE unique_key = value;

-- Method 2.
SELECT COUNT(1)
FROM table_name
WHERE unique_key = value;

最初の代替案では結果がないか1つの結果が得られ、2番目のカウントは0または1になります。

使用しているドキュメントは何歳ですか?良いアドバイスを読みましたが、最近のRDBMSのほとんどのクエリオプティマイザーはSELECT COUNT(*)とにかく最適化するため、理論(および古いデータベース)には違いがありますが、実際には違いに気付かないはずです。


1
「一意のキー」を「key = value」句で意図していたことを明確にしますが、それ以外はまだ回答が遅れています。
Martin Schapendonk

1
OK。その前提では、確かにクエリは1つまたは0つのレコードのみを返します。ただし、質問は一意の列に限定されません。また、2番目のクエリcount(1)は、実用的なPOVのcount(*)と同等です。
Martin Ba

1
「Aレコードの存在を確認する適切な方法は何か」という質問です。1つのレコードのように、それを単数形として解釈しました。count(*)とcount(1)の違いはすでに私の回答でカバーされています。特定のRDBMS実装に依存しないため、私はcount(1)を好みます。
Martin Schapendonk

192

私はカウント関数をまったく使用しないことを望みます:

IF [NOT] EXISTS ( SELECT 1 FROM MyTable WHERE ... )
     <do smth>

たとえば、データベースに挿入する前にユーザーが存在するかどうかを確認する場合、クエリは次のようになります。

IF NOT EXISTS ( SELECT 1 FROM Users WHERE FirstName = 'John' AND LastName = 'Smith' )
BEGIN
    INSERT INTO Users (FirstName, LastName) VALUES ('John', 'Smith')
END

一般的に私たちは何かをしたいときにそれを使用します(検証)、そしてあなたの答えはより完全です。
AbnerEscócio2018年

T-SQLを使用することにより、
Bronek

20

以下を使用できます。

SELECT 1 FROM MyTable WHERE <MyCondition>

条件に一致するレコードがない場合、結果のレコードセットは空です。


TOP 1ですか?->(MyTable WHERE <MyCondition>からTOP 1を選択)
Jacob

6
いいえ、私は正確に「1」を意味
カタリンPitiş

1
クエリオプティマイザーが残りのデータセットを読み取らない、または必要としないことを有効にするには、SELECT TOP 1 1 FROM ... WHERE ...と記述する必要があります(またはRDBSに適切なクエリヒントを使用します)
eFloh

3
Existsオペレーター自体は、最小限の情報だけを取得しようとするため、TOP 1を追加しても、クエリサイズに5文字が追加されるだけです。- sqlservercentral.com/blogs/sqlinthewild/2011/04/05/...
AquaAlex

13

他の答えはかなり良いですが、不要な行のチェックを防ぐために追加するLIMIT 1(または同等の)ことも役立ちます。


3
「存在の確認」クエリが複数の行を返す場合、結果の数を制限するよりも、WHERE句を再確認する方が便利だと思います。
Martin Schapendonk 2010年

2
LimitはSQL ServerではなくOracleで使用されていると思います
Shantanu Gupta 2010

7
私はそれらが合法的に複数の行になる可能性がある場合を検討しています-質問は次のとおりです:「この条件を満たす行は(1つ以上)ありますか?」その場合は、それらすべてではなく、1つだけを確認する必要があります。
JesseW 2010年

1
@シャンタヌ-私が知っているのは、私が他のフォームを説明するen.wikipediaの記事にリンクしている理由です。
JesseW 2010年

11
SELECT COUNT(1) FROM MyTable WHERE ...

すべてのレコードをループします。これが、レコードの存在に使用するのが悪い理由です。

私は使うだろう

SELECT TOP 1 * FROM MyTable WHERE ...

1つのレコードを見つけた後、ループを終了します。


以下の場合、SELECT TOP 1それは実際には1つを発見した後に終了するか、それはすべてがTOPどちらであると言うことができるように見つけるために続けていますか?
Eirik H

3
PS:私がいつも確認していることIF EXISTS (SELECT TOP 1 1 FROM ... WHERE ..)
Eirik H

スター演算子は、ジョイン条件に必要なインデックスだけでなく、DBMSにクラスター化インデックスへのアクセスを強制します。したがって、結果として定数評価を使用することをお勧めします。つまり、select top 1 1 ...です。条件が一致するかどうかに応じて、1またはDB-Nullを返します。
eFloh

いいね。私は最初のものが好きです。
isxaker 2018

10

以下を使用できます。

SELECT COUNT(1) FROM MyTable WHERE ... 

または

WHERE [NOT] EXISTS 
( SELECT 1 FROM MyTable WHERE ... )

これSELECT *は、すべてのフィールドではなく、各行の値1を選択するだけなので、より効率的です。

COUNT(*)とCOUNT(列名)の間にも微妙な違いがあります:

  • COUNT(*) nullを含むすべての行をカウントします
  • COUNT(column name)列名のnull以外の出現のみをカウントします

2
あなたは、DBMSがこれらの列をすべて何らかの方法でチェックするという誤った仮定を行っています。性能差count(1)とは、count(*)唯一の最も脳死DBMSでは異なるであろう。
paxdiablo 2010年

2
いいえ、私はあなたがそれがより効率的であると述べるときあなたが実際に実装の詳細に依存していると言っています。最高のパフォーマンスを確実に実現したい場合は、代表的なデータを使用して特定の実装用にプロファイルするか、完全に忘れてください。それ以外のものは誤解を招く可能性があり、DB2からMySQLに(たとえば)移動すると大幅に変わる可能性があります。
paxdiablo 2010年

1
私はあなたの答えを否定しないことを明確にしたいと思います。それはある便利。唯一の私たちがきたので、効率の主張であるとの問題を取るビット行わ DB2 / Zで評価をしての間には実質的な違いがありますが見つかりませんcount(*)とはcount(1)それが他の DBMSに当てはまるかどうか、私には言えません。
paxdiablo 2010年

3
「それ以外のものは誤解を招く可能性があり、(たとえば)DB2からMySQLに移動すると大幅に変更される可能性があります」 SELECT 1の実装の違いよりも、DBMSを移動するときにSELECT COUNT(*)のパフォーマンスの低下に噛まれる可能性がはるかに高くなります。またはCOUNT(1)。私は、オプティマイザやコンパイラに頼ってデフォルトの動作に頼るのではなく、達成したいことを最も明確に表すコードを書くことに強い信念を持っています。
Winston Smith、

1
誤解を招くステートメント「COUNT(*)」は、「行をカウントする」という完全な停止を意味します。特定の列にアクセスする必要はありません。そして、ほとんどの場合、カウントは一意のインデックスで十分であるため、行自体へのアクセスを必要としません。
James Anderson

9

以下を使用できます。

SELECT 1 FROM MyTable WHERE... LIMIT 1

使用する select 1不要なフィールドのチェックを防ぐためにします。

LIMIT 1 不要な行のチェックを防ぐために使用します。


3
良い点ですが、制限はMySQLとPostgreSQLで機能し、上部はSQL Serverで機能します。回答にそれを記録する必要があります
Leo Gurdian

0

私はこの方法を使用しています:

IIF(EXISTS (SELECT TOP 1 1 
                FROM Users 
                WHERE FirstName = 'John'), 1, 0) AS DoesJohnExist

0

その他のオプション:

SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [MyTable] AS [MyRecord])
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.