SQL結合とSQLサブクエリ(パフォーマンス)の比較


110

私がこのような結合クエリを持っているかどうか知りたい-

Select E.Id,E.Name from Employee E join Dept D on E.DeptId=D.Id

そして、このようなサブクエリ -

Select E.Id,E.Name from Employee Where DeptId in (Select Id from Dept)

パフォーマンスを検討するとき、2つのクエリのどちらが高速で、なぜですか?

また、どちらか一方を優先する必要がある時期はありますか?

これが簡単すぎて前に尋ねられたら申し訳ありませんが、私はそれについて混乱しています。また、2つのクエリのパフォーマンスを測定するために使用するツールを提案していただければ幸いです。どうもありがとう!


5
@Lucero、この質問にはsql-server-2008のタグが付けられており、あなたが言及している投稿にはMySqlのタグが付けられています。答えは同じになると推測できます。パフォーマンスの最適化は、2つのRDBMSで異なる方法で行われます。
Francois Botha

回答:


48

最初のクエリの方が速いと思いますが、これは主に、等価で明示的なJOINがあるためです。私の経験でINは、SQLは通常WHERE、 "OR"(WHERE x=Y OR x=Z OR...)で区切られた一連の句として評価するため、非常に遅い演算子です。

ただし、ALL THINGS SQLと同様に、走行距離は異なる場合があります。速度は、とりわけインデックスに大きく依存します(両方のID列にインデックスがありますか?それは非常に役立ちます...)。

100%の確実性で高速に伝える唯一の実際の方法は、パフォーマンス追跡を有効にして(IO統計が特に有用です)、両方を実行することです。実行と実行の間に必ずキャッシュをクリアしてください!


16
ほとんどのDBMS、間違いなくSQL Server 2008以降では、単一のIDサブクエリ(相関関係がない、つまり複数の外部クエリ列を参照していない)を比較的高速の準結合に変換するため、この回答には深刻な疑いがあります。また、別の回答で前述したように、最初の実際の結合では、Deptで一致するIDが発生するたびに行が返されます。これにより、一意のIDに違いはありませんが、他の場所では大量の重複が発生します。これらをDISTINCTまたはGROUP BYでソートすると、パフォーマンスの負荷が大きくなります。SQL Server Management Studioで実行プランを確認してください!
Erik Hart、

2
ORと同等のIN句はパラメーター/値リストに適用されますが、ほとんどが結合のように扱われるサブクエリには適用されません。
Erik Hart、

42

まあ、それは「古くて金」の質問だと思います。答えは、「場合によります」です。パフォーマンスは非常にデリケートなテーマであり、「サブクエリを使用せず、常に参加する」と言っても馬鹿げているでしょう。次のリンクには、私が非常に役立つとわかった基本的なベストプラクティスがいくつかあります。

50000要素のテーブルがあり、探していた結果は739要素でした。

私の最初のクエリはこれでした:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND p.anno = (
    SELECT MAX(p2.anno) 
    FROM prodotto p2 
    WHERE p2.fixedId = p.fixedId 
)

実行に7.9秒かかりました。

最後に私のクエリはこれです:

SELECT  p.id,
    p.fixedId,
    p.azienda_id,
    p.categoria_id,
    p.linea,
    p.tipo,
    p.nome
FROM prodotto p
WHERE p.azienda_id = 2699 AND (p.fixedId, p.anno) IN
(
    SELECT p2.fixedId, MAX(p2.anno)
    FROM prodotto p2
    WHERE p.azienda_id = p2.azienda_id
    GROUP BY p2.fixedId
)

そしてそれは0.0256秒かかりました

良いSQLです。


3
興味深いのですが、GROUP BYを追加することでどのように修正されたかを説明できますか?
cozos

6
サブクエリによって生成された一時テーブルが小さくなりました。チェックするために以下のデータがあるので、そこで実行が高速です。
Sirmyself

2
最初のクエリでは外部クエリとサブクエリの間で変数を共有していると思うので、メインクエリのすべての行でサブクエリが実行されますが、2番目のクエリではサブクエリが1回しか実行されず、このようにしてパフォーマンスが向上しました。
Ali Faradjpour

1
SQLサーバーとMySqlおよび... Sql(NoSqlを除く)は、インフラストラクチャーが非常に似ています。その下には、IN(...)句を結合に変換する(可能な場合)一種のクエリ最適化エンジンがあります。しかし、(基数に基づいて)インデックスが適切に作成された列にGroup byがある場合は、はるかに高速になります。したがって、それは本当に状況に依存します。
Alix

10

SQlサーバーがそれらを解釈する方法の違いを確認するために、実行プランを確認し始めます。プロファイラーを使用して実際にクエリを複数回実行し、差異を取得することもできます。

相関関係のあるサブクエリを使用すると、サブクエリの代わりに結合を使用することで実際に大幅なパフォーマンスの向上を得ることができるので、これらがそれほど大きく異なることはないと思います。

多くの場合、EXISTSはこれらの2つのどちらよりも優れており、左結合テーブルにないすべてのレコードが必要な左結合について話している場合、NOT EXISTSの方がはるかに良い選択です。


9

パフォーマンスは、実行しているデータの量に基づいています...

20kあたりのデータが少ない場合。JOINはよりよく機能します。

データが100k +に近い場合、INはより適切に機能します。

他のテーブルのデータが必要ない場合は、INで十分ですが、EXISTSを使用することをお勧めします。

私がテストしたこれらすべての基準とテーブルには適切なインデックスがあります。


4

パフォーマンスは同じでなければなりません。テーブルに適切なインデックスとクラスタリングを適用することの方がはるかに重要です(適切なリソースいくつか存在します)そのトピックにはがあります)。

(更新された質問を反映するように編集)


4

2つのクエリは、意味的に同等ではない場合があります。従業員が複数の部門で働いている場合(私が働いている企業で可能性があります。確かに、これはテーブルが完全に正規化されていないことを意味します)、最初のクエリは重複する行を返しますが、2番目のクエリは返しません。この場合、クエリを同等にするには、DISTINCTキーワードをSELECT句に必要があり、パフォーマンスに影響を与える可能性があります。

テーブルにはエンティティ/クラスまたはエンティティ/クラス間の関係をモデル化する必要があるが、両方をモデル化するべきではないという設計上の経験則があることに注意してください。したがって、私はOrgChart、従業員と部門間の関係をモデル化するための3番目のテーブルを作成することをお勧めします。


4

私はこれが古い投稿であることは知っていますが、これは非常に重要なトピックだと思います。特に、現在1,000万以上のレコードがあり、テラバイトのデータについて話している今日では特にそうです。

私はまた、以下の観察に重みを付けます。テーブル([data])に約45Mのレコードがあり、[cats]テーブルに約300のレコードがあります。これからお話しするすべてのクエリに対して、広範なインデックスを作成しています。

例1を考えます。

UPDATE d set category = c.categoryname
FROM [data] d
JOIN [cats] c on c.id = d.catid

例2:

UPDATE d set category = (SELECT TOP(1) c.categoryname FROM [cats] c where c.id = d.catid)
FROM [data] d

例1の実行には約23分かかりました。例2では約5分かかりました。

したがって、この場合のサブクエリははるかに高速であると結論付けます。もちろん、I / O @ 1GB /秒(ビットではなくバイト)が可能なM.2 SSDドライブを使用しているため、インデックスも非常に高速であることを覚えておいてください。したがって、これはあなたの状況では速度にも影響を与える可能性があります

1回限りのデータクレンジングの場合、おそらく実行したままにして終了するのが最善です。TOP(10000)を使用して、大きなクエリに到達するまでにかかる時間とレコード数を乗算する時間を確認します。

運用データベースを最適化している場合は、データを前処理することを強くお勧めします。つまり、トリガーまたはジョブブローカーを使用して更新レコードを非同期にし、リアルタイムアクセスが静的データを取得できるようにします。


0

Explain Planを使用して、客観的な答えを得ることができます。

あなたの問題では、Existsフィルターがおそらく最も速く実行されます。


2
「Existsフィルターはおそらく最も高速に実行されます」-おそらくそうではないと思いますが、決定的な答えには実際のデータに対するテストが必要です。同じルックアップ値を持つ複数の行がある場合、Existsフィルターはより高速になる可能性があります。したがって、クエリが他の従業員が同じ部門から記録されているかどうかをチェックしている場合、existsフィルターはより速く実行される可能性がありますが、おそらく部門に対して検索するときはそうではありません。テーブル。

最後のシナリオでは、実行速度が遅くなりますか?
Snekse

それはオプティマイザに依存します-特定の状況下ではそれはそうかもしれませんが、通常私は非常に類似したパフォーマンスを期待します。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.