より効率的なのは、where句または100万行以上のテーブルとの結合ですか?


17

1つのテーブルに250MMの行があり、ほとんどのクエリで結合する別のテーブルに15MM未満の行があるWebサイトを実行します。

サンプル構造:

MasterTable (Id, UserId, Created, Updated...) -- 15MM Rows
DetailsTable (Id, MasterId, SomeColumn...) -- 250MM Rows
UserTable (Id, Role, Created, UserName...) -- 12K Rows

これらすべてのテーブルに対していくつかのクエリを定期的に行う必要があります。1つは、無料ユーザー(最大1万人の無料ユーザー)の統計を取得することです。

Select Count(1) from DetailsTable dt 
join MasterTable mt on mt.Id = dt.MasterId 
join UserTable ut on ut.Id = mt.UserId 
where ut.Role is null and mt.created between @date1 and @date2

問題は、結合がどこよりもずっと前に発生するという事実のために、このクエリが何度も長時間実行されることです。

この場合、結合または場合によってはwheresを使用する方が賢明でしょうwhere column in(...)か?


1
どのデータベースとバージョンですか?
リーリッフェル

1
両方の方法を試しましたか?
gbn

これがOracleの場合、NVL2(Role、NULL、ID)にUserTableの関数ベースのインデックスを作成しますが、これは別のDBのように見えます。
リーリッフェル

回答:


20

最新のRDBMSの場合、パフォーマンスとクエリプランに関して、「明示的な結合」と「JOIN-in-the-WHERE」(すべての結合が内部の場合)の間に違いはありません。

明示的なJOIN構文はより明確であいまいではありません(以下のリンクを参照)

さて、JOIN-before-WHEREは実際の処理ではなく論理処理であり、現代のオプティマイザーはこれを実現するのに十分賢いです。

ここでの問題は、おそらく索引付けです。

これらのテーブルのすべてのインデックスとキーを表示してください。そしてクエリプラン

注:この質問は、StackOverflowで今までは重複していたので近づいていました... COUNT(1)vs COUNT(*)も別の神話です。


2
joinand where句に違いがないことは常に真実ではありません。長時間実行されるクエリは常に最適化され、クエリを使用するクエリは、使用するクエリwhereよりもjoin最大70倍のパフォーマンスを発揮する場合があります。それがそんなにシンプルで簡単だったら、人生はすべて虹とユニコーンになるでしょう。そして、これは古代の曖昧なエンジンに関するものではありません-今のところwhere、SQL 2012 の条項の
70倍の

さらに、私はしばしば両方のアプローチからまったく同じ計画を観察し、クエリがまったく同じように実行されるように分離しwhereますが、句クエリが大規模バッチ内で実行されると、その一部であると想定され、joinクエリよりも大幅に優れています。SQLクエリは真空では実行されません-サーバーペイロードの残りの影響を受けます。また、多くの場合、where句クエリは非常にうまく機能しjoinます。
アジェ

3
@ajeh:あなたの経験は非常に典型的ではないことをお勧めします。x70の違いがある場合、クエリには大きな問題があります。それは単純です
-gbn

5

クエリを完全にリファクタリングする必要があります

早くWHERE句を実行し、後でJOINを実行してみてください

Select Count(1) from DetailsTable dt
join (Select UserId,Id FROM MasterTable where
created between @date1 and @date2) mt on mt.Id = dt.MasterId 
join (Select Id FROM UserTable WHERE Role is NULL) ut
on ut.Id = mt.UserId;

このリファクタリングされたクエリでEXPLAINプランを実行し、元のクエリよりも悪く見える場合でも、とにかく試してください。内部で作成された一時テーブルはデカルト結合を実行しますが、それらのテーブルは作業するために小さくなります。

私はこのYouTubeビデオからこのアイデアを得ました

StackOverflowの非常に複雑な質問でビデオの原則を試し、200ポイントの賞金を獲得しました。

@gbnは、適切なインデックスが適切に配置されていることを確認することについて言及しました。この場合、MasterTableで作成された列にインデックスを付けてください。

試してみる !!!

更新2011-06-24 22:31 EDT

次のクエリを実行する必要があります。

SELECT COUNT(1) AllRoles FROM UserTable;
SELECT COUNT(1) NullRoles FROM UserTable WHERE Role is NULL;

NullRoles X 20 <AllRolesの場合(つまり、NullRolesがテーブル行の5%未満の場合)、UserTableでRoleの一意でないインデックスを作成する必要があります。そうしないと、クエリオプティマイザーがインデックスの使用を除外する可能性があるため、UserTableの完全なテーブルで十分です。

更新2011-06-25 12:40 EDT

私はMySQL DBAであるため、物事を行う私の方法は、肯定的な悲観論と保守的であることを通じてMySQL Query Optimizerを信頼しないことを必要とします。したがって、クエリをリファクタリングするか、必要なカバーインデックスを作成して、MySQLクエリオプティマイザーの隠れた悪い習慣に先んじてみます。@gbnの答えは、SQL Serverがクエリを評価する「心の健全性」を高める可能性があるという点で、より完全に思えます。


0

約7,500万行の[Detail]テーブルがありました。[マスター]テーブルは約40万行で、関連する[アイテム]テーブルは常に7行でした。「アイテム番号」(1〜7)の小さなセットを保存し、毎月数百万個が印刷および配布される紙のフォームをモデリングしていました。最速のクエリは、デカルト結合の使用を含む、最初に考える可能性が最も低いクエリでした。IIRC、それは次のようなものでした:

SELECT m.order_id, i.line_nr, d.Item_amt
FROM Master m, Item i 
INNER JOIN Detail d ON m.order_id = d.order_id

[Item]と[Detail]の間に論理的な「id」リンクがありますが、CROSS JOINはINNER JOINよりもうまく機能しました。

RDBMSは、MPPテクノロジを備えたTeradataであり、IDRはインデックススキームでした。TABLE SCANは常に最高のパフォーマンスを発揮したため、7行のテーブルにはインデックスがありませんでした。

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