web-dev-qa-db-ja.com

トップ1を選択* vsトップ1 1を選択

私はこれらの質問がたくさんあることを知っていますが、私の質問に関連する質問を見つけることができません。

この質問を見て、 IF EXIST(SELECT 1 FROM)をIF EXIST(SELECT TOP 1 FROM)に変更すると副作用がありますか?

回答のこのセクションを具体的に参照してください:

select * from sys.objects
select top 1 * from sys.objects
select 1 where exists(select * from sys.objects)
select 1 where exists(select top 1 * from sys.objects)

私はそれを正しく理解するためにいくつかの独自のテストを実行しています。答えに示されているように:

select 1 where exists(select top 1 * from sys.objects)
select 1 where exists(select top 1 1 from sys.objects)

どちらも同じ実行プランを引き起こし、同じように同じプランを引き起こします

select 1 where exists(select * from sys.objects)
select 1 where exists(select 1 from sys.objects)

このような質問についての私の調査から、 "SELECT TOP 1 1" VS "IF EXISTS(SELECT 1" 。これが合意されたベストプラクティスであると推測しています:

select 1 where exists(select * from sys.objects)

私の最初の質問は、これがこれより好ましい理由です:

select 1 where exists(select 1 from sys.objects)

それを理解しようとする中で、私は彼らをより基本的な表現に分解しました(私は「トップ1」を使用して、類似の実行プランを模倣しています):

select top 1 * from sys.objects
select top 1 1 from sys.objects

1つ目は実行時間の80%(2のバッチに対して)であり、2つ目はわずか20%であることがわかります。それを使用するのは良い習慣ではないでしょうか

select 1 where exists(select 1 from sys.objects)

それは両方のシナリオに適用でき、それによって起こり得る人的エラーを減らすことができるからですか?

8
Storm

SQL Serverは、クエリのコンパイル/最適化プロセスの比較的早い段階でEXISTS述語を検出し、そのような句の実際のデータ取得を排除して、存在チェックに置き換えます。だからあなたの仮定:

1つ目は実行時間の80%(2のバッチに対して)であり、2つ目はわずか20%であることがわかります。

上記の比較では実際に一部のデータを取得しているため、これは誤りです。クエリが(not) exists述語。

ほとんどの場合、1つの重要なキャッチを除いて、行の存在をテストする方法に違いはありません。あなたが言うと仮定します:

if exists (select * from dbo.SomeTable)
...

コードモジュールのどこか(ビュー、ストアドプロシージャ、関数など)。その後、他の誰かがWITH SCHEMABINDING句をこのコードモジュールに挿入すると、SQL Serverはそれを許可せず、列の現在のリストにバインドする代わりにエラーをスローします。

メッセージ1054、レベル15、状態7、プロシージャBoundView、行6
構文「*」はスキーマバインドオブジェクトでは使用できません。

つまり、要するに:

if exists (select 0 from ...)

存在チェックのための最も安全で最速かつ万能の方法です。

9
Roger Wolf

これら2つの違い:

select top 1 * from sys.objects
select top 1 1 from sys.objects

つまり、最初の句でSQLサーバーはすべての列をテーブルから(任意のランダムな行から)フェッチする必要がありますが、2番目の句では、任意のインデックスから "1"をフェッチしても問題ありません。

これらの句がexists句の内側にあると状況が変わります。その場合、SQL Serverは何にも割り当てられないためデータを実際にフェッチする必要がないことを認識しているため、select *を同じように処理できます。 select 1を処理します。

Existsは1行しかチェックしないため、内部のトップ1が組み込まれているため、手動で追加しても何も変更されません。

Exists句にselect *またはselect 1を含める天気は、単に意見に基づいており、1の代わりに、もちろん2または 'X'またはその他の好きなものを使用できます。個人的には常に... and exists (select 1 ...を使用します

3
James Z

EXISTSは、サブクエリによって行が返されるかどうかに基づいてブール値のみを返すことができるサブクエリのタイプです。結果は常にtrueまたはfalseになるため、1、*、またはこのコンテキスト内で重要ではないものを選択します。

これら2つのステートメントがまったく同じ計画を生成することをテストすることで、これを確認できます。

select 1 where exists(select * from sys.objects)
select 1 where exists(select 1 from sys.objects)

外部クエリで何を選択するかが重要です。ご覧のとおり、これらの2つのステートメントは非常に異なる実行プランを生成します。

select top 1 * from sys.objects
select top 1 1 from sys.objects

最初のものは実際に実際のデータを返さなければならないので遅くなります。この場合、基になる3つのテーブル(syspalnames、syssingleobjrefs、sysschobjs)に結合します。

EXISTSサブクエリ内に何を配置するか(SELECT 1またはSELECT *)の設定は重要ではありません。私は通常SELECT 1と言いますが、SELECT *も同様に優れており、多くのMicrosoftのドキュメントに記載されています。

3
PeterDNCO

タイトルに含まれている実際の質問に対するjustの回答を探していました。私はそれを見つけました このリンクで

Top 1またはTop nを選択すると、基本的にSQLクエリに基づいてデータの最初のn行が返されます。 Top 1 1またはTop n sを選択すると、SQLクエリに応じて、データsを含む最初のn行が返されます。

たとえば、次のクエリは、最初の10件の一致の姓と名を生成します。このクエリは、名と姓のみを返します。

SELECT TOP 10 FirstName, LastName
  FROM [tblUser]
  where EmailAddress like 'john%'

ここで、select top 10 'test'でこのクエリを見てください。これにより、前のクエリと同じ行数(同じデータベース、同じ条件)が生成されますが、値は 'test'になります。

SELECT TOP 10 'test'
  FROM [tblUser]
  where EmailAddress like 'john%'

そう、 select TOP 1 *は最初の行を返し、select TOP 1 1は、「1」だけを含む1つのrowを返します。これは、クエリが少なくとも1つの行を返す場合、それ以外の場合は Nullがどちらの場合でも返されます

追加の例として、これは:

SELECT TOP 10 'test', FirstName
  FROM [tblUser]
  where EmailAddress like 'john%'

「test」で満たされた列と、クエリの最初の10件の一致の名で満たされた別の列を含むテーブルを返します。

0
Antonio