web-dev-qa-db-ja.com

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

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

サンプル構造:

_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
_

問題は、結合がwhereよりもずっと前に発生するため、このクエリで長い時間がかかることがあります。

この場合、結合の代わりにwheresを使用するほうが賢明でしょうか、おそらくwhere column in(...)

20
Jeremy Boyd

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

明示的なJOIN構文はより明確で曖昧さが少なくなっています(以下のリンクを参照)

今、JOIN-before-WHEREはlogical処理ではなくactual処理ですそして現代のオプティマイザはこれを実現するのに十分賢いです。

ここでの問題はおそらくインデックス化です。

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

注:この質問は、重複しているためStackOverflowでこれまでに終了していたでしょう... COUNT(1)とCOUNT(*)もまた、間違った神話です。

20
gbn

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

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にインデックス付けしてください。

試してみる !!!

UPDATE 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でロールに一意でないインデックスを作成する必要があります。それ以外の場合は、クエリオプティマイザがインデックスを使用しない可能性があるため、UserTableの完全なテーブルで十分です。

UPDATE 2011-06-25 12:40 EDT

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

6
RolandoMySQLDBA

[Detail]テーブルには約7,500万行ありました。約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行のテーブルにはインデックスがありませんでした。

1
Timothy Oleary