JOINクエリは、いくつかのクエリよりも高速ですか?(メインクエリを実行してから、メインクエリの結果に基づいて他の多くのSELECTを実行します)
それらを結合するとアプリケーションの設計が非常に複雑になるので、私は尋ねています
より速い場合、だれがどれだけ大まかに概算できますか?1.5倍の場合は気にしませんが、10倍の場合はそうだと思います。
JOINクエリは、いくつかのクエリよりも高速ですか?(メインクエリを実行してから、メインクエリの結果に基づいて他の多くのSELECTを実行します)
それらを結合するとアプリケーションの設計が非常に複雑になるので、私は尋ねています
より速い場合、だれがどれだけ大まかに概算できますか?1.5倍の場合は気にしませんが、10倍の場合はそうだと思います。
回答:
これはあいまいすぎて、特定のケースに関連する回答を提供できません。それは多くのものに依存します。Jeff Atwood(このサイトの創設者)が実際にこれについて書いた。ただし、ほとんどの場合、適切なインデックスがあり、JOINを適切に実行すると、通常、数回のトリップよりも1回のトリップの方が速くなります。
内部結合の場合、一致する行のみを取得するため、単一のクエリは理にかなっています。左結合については、複数のクエリの方がはるかに優れています...私が行った次のベンチマークを見てください:
5つの結合を持つ単一のクエリ
クエリ:8.074508秒
結果サイズ:2268000
続けて5つのクエリ
クエリの合計時間:0.00262秒
結果のサイズ:165(6 + 50 + 7 + 12 + 90)
。
どちらの場合も同じ結果が得られることに注意してください(6 x 50 x 7 x 12 x 90 = 2268000)
左結合は、冗長なデータで指数関数的に多くのメモリを使用します。
2つのテーブルの結合のみを実行する場合、メモリ制限はそれほど悪くはないかもしれませんが、通常は3つ以上であり、異なるクエリの価値があります。
ちなみに、MySQLサーバーはアプリケーションサーバーのすぐ横にあるので、接続時間はごくわずかです。接続時間が秒単位の場合、おそらくメリットがあります
フランク
この質問は古いですが、いくつかのベンチマークがありません。JOINを2つの競合他社に対してベンチマークしました。
WHERE IN(...)
同等または同等のものを使用結果は明らかです:MySQLの上で、JOIN
ある非常に速いです。N + 1クエリは、アプリケーションのパフォーマンスを大幅に低下させる可能性があります。
つまり、非常に少数の個別の外部レコードを指す多くのレコードを選択しない限りです。以下は、極端なケースのベンチマークです。
多対多のリレーションシップに参加している場合を除いて、これは一般的なアプリケーションで発生する可能性はほとんどありません。その場合、外部キーは他のテーブルにあり、メインテーブルのデータを何度も複製します。
取り除く:
JOIN
実際に私は自分自身で答えを探してこの質問に行きました。与えられた答えを読んだ後、DBクエリのパフォーマンスを比較する最善の方法は、考慮すべき変数が多すぎるため、実際の数値を取得することであることにのみ同意できますしかし、それらの間の数を比較することは、ほとんどすべての場合に不利益につながると私は思います。つまり、数値は常に許容可能な数値と比較されるべきであり、明らかに互いに比較されるべきではないということです。
クエリの1つの方法に0.02秒かかる場合と、もう1つの方法に20秒かかる場合、それは大きな違いです。しかし、クエリの1つの方法に0.0000000002秒かかり、もう1つの方法に0.0000002秒かかる場合はどうでしょうか。どちらの場合も、一方の方法はもう一方の方法よりもなんと1000倍高速ですが、2番目の方法でも本当に「なんとなく」そうでしょうか。
個人的に見た結果の要点:パフォーマンスが良い場合は、簡単な解決策を検討してください。
50,000行のテーブルから1行を選択し、100,000行のテーブルから1行と結合する簡単なテストを行いました。基本的には次のように見えました:
$id = mt_rand(1, 50000);
$row = $db->fetchOne("SELECT * FROM table1 WHERE id = " . $id);
$row = $db->fetchOne("SELECT * FROM table2 WHERE other_id = " . $row['other_id']);
対
$id = mt_rand(1, 50000);
$db->fetchOne("SELECT table1.*, table2.*
FROM table1
LEFT JOIN table1.other_id = table2.other_id
WHERE table1.id = " . $id);
2つの選択方法では、50,000回の読み取りに3.7秒かかりましたが、自宅の遅いコンピューターではJOINに2.0秒かかりました。INNER JOINとLEFT JOINは違いがありませんでした。複数の行をフェッチすると(たとえば、IN SETを使用して)、同様の結果が得られました。
本当の質問は次のとおりです。これらのレコードには1対1の関係または1対多の関係がありますか?
TLDR回答:
1対1の場合は、JOIN
ステートメントを使用します。
1対多の場合はSELECT
、サーバー側のコード最適化で1つ(または複数)のステートメントを使用します。
SELECTを最適化に使用する理由と方法
SELECT
1対多の関係に基づいてレコードの大規模なグループに対して(結合ではなく複数のクエリを使用して)実行JOIN
すると、指数関数的なメモリリークの問題があるため、最適な効率が得られます。すべてのデータを取得し、サーバー側のスクリプト言語を使用してデータを整理します。
SELECT * FROM Address WHERE Personid IN(1,2,3);
結果:
Address.id : 1 // First person and their address
Address.Personid : 1
Address.City : "Boston"
Address.id : 2 // First person's second address
Address.Personid : 1
Address.City : "New York"
Address.id : 3 // Second person's address
Address.Personid : 2
Address.City : "Barcelona"
ここでは、1つのselectステートメントですべてのレコードを取得しています。これはJOIN
、これらのレコードの小さなグループを1つずつ、別のクエリのサブコンポーネントとして取得するよりも優れています。次に、次のようなサーバー側コードで解析します...
<?php
foreach($addresses as $address) {
$persons[$address['Personid']]->Address[] = $address;
}
?>
最適化にJOINを使用しない場合
JOIN
単一のレコードとの1対1の関係に基づいてレコードの大きなグループを作成するとSELECT
、次のレコードタイプを取得するだけの複数のステートメントと比較して、最適な効率が得られます。
ただしJOIN
、1対多の関係でレコードを取得する場合は非効率的です。
例:データベースBlogsには、Blogpost、Tag、およびCommentの3つの対象テーブルがあります。
SELECT * from BlogPost
LEFT JOIN Tag ON Tag.BlogPostid = BlogPost.id
LEFT JOIN Comment ON Comment.BlogPostid = BlogPost.id;
ブログ投稿が1つ、タグが2つ、コメントが2つある場合、次のような結果が得られます。
Row1: tag1, comment1,
Row2: tag1, comment2,
Row3: tag2, comment1,
Row4: tag2, comment2,
各レコードがどのように複製されているかに注意してください。わかりましたので、2つのコメントと2つのタグは4行です。4つのコメントと4つのタグがある場合はどうなりますか?あなたは8行を取得しません-あなたは16行を取得します:
Row1: tag1, comment1,
Row2: tag1, comment2,
Row3: tag1, comment3,
Row4: tag1, comment4,
Row5: tag2, comment1,
Row6: tag2, comment2,
Row7: tag2, comment3,
Row8: tag2, comment4,
Row9: tag3, comment1,
Row10: tag3, comment2,
Row11: tag3, comment3,
Row12: tag3, comment4,
Row13: tag4, comment1,
Row14: tag4, comment2,
Row15: tag4, comment3,
Row16: tag4, comment4,
テーブルやレコードなどを追加すると、問題はすぐに数百の行に広がり、ほとんどすべてが冗長なデータでいっぱいになります。
これらの複製はあなたにどのような費用がかかりますか?メモリ(SQLサーバーと重複を削除しようとするコード内)およびネットワークリソース(SQLサーバーとコードサーバー間)。
ソース:https : //dev.mysql.com/doc/refman/8.0/en/nested-join-optimization.html ; https://dev.mysql.com/doc/workbench/en/wb-relationship-tools.html
個別のクエリと結合の両方を作成してから、それぞれの時間を計測します。実際の数値以外に役立つものはありません。
次に、さらに良い-各クエリの先頭に「EXPLAIN」を追加します。これにより、MySQLがデータのリクエストに応答するために使用しているサブクエリの数、および各クエリでスキャンされた行の数がわかります。
私の経験では、特に大規模なデータセットを取得する場合は、通常、複数のクエリを実行する方が高速であることがわかりました。
PHPなどの別のアプリケーションからデータベースを操作する場合、何回もサーバーにアクセスするという議論があります。
サーバーへのトリップの数を制限し、複数のクエリを実行する方法は他にもあります。多くの場合、クエリは高速であるだけでなく、アプリケーションを読みやすくすることもできます(例:mysqli_multi_query)。
私はSQLに関しては初心者ではありません。開発者、特に後輩はスマートに見えるため、非常に巧妙な結合を作成しようとすることに多くの時間を費やす傾向があると思いますが、実際に見えるデータを抽出するスマートな方法がありますシンプル。
最後の段落は個人的な意見でしたが、これが役に立てば幸いです。ベンチマークを行うべきだと言う人もいますが、私は他の人には同意します。どちらのアプローチも特効薬ではありません。
結合を使用する必要があるかどうかは、何よりもまず、結合が理にかなっているかどうかについてです。他のほとんどすべてのケースでは、パフォーマンスが大幅に低下するため、その時点でのみ、考慮すべき何かであってもパフォーマンスがあります。
パフォーマンスの違いは、照会する情報の関連性に大きく関係しています。結合は機能し、データが関連付けられており、データに正しくインデックスを付けると結合は高速になりますが、結合はしばしば冗長性をもたらし、時には必要以上の結果をもたらします。また、データセットが直接関連していない場合、それらを1つのクエリに貼り付けると、デカルト積(基本的には行のすべての可能な組み合わせ)と呼ばれる結果が得られますが、これはほとんど望んでいることではありません。
これは多くの場合、多対1対多の関係によって引き起こされます。たとえば、HoldOffHungerの回答には、投稿、タグ、コメントに対する単一のクエリが含まれていました。タグと同様に、コメントは投稿に関連していますが、タグはコメントに関連していません。
+------------+ +---------+ +---------+
| comment | | post | | tag |
|------------|* 1|---------|1 *|---------|
| post_id |-----| post_id |-----| post_id |
| comment_id | | ... | | tag_id |
| user_id | | | | ... |
| ... | | | | ... |
+------------+ +---------+ +---------+
この場合、少なくとも2つの個別のクエリであることが明白です。タグとコメントを結合しようとすると、2つの間に直接の関係がないため、タグとコメントの可能なすべての組み合わせになります。many * many == manymany
。それとは別に、投稿とタグは無関係であるため、これら2つのクエリを並行して行うことができ、潜在的な利益につながります。
ただし、別のシナリオについて考えてみましょう。投稿にコメントを添付し、コメント投稿者の連絡先情報を取得します。
+----------+ +------------+ +---------+
| user | | comment | | post |
|----------|1 *|------------|* 1|---------|
| user_id |-----| post_id |-----| post_id |
| username | | user_id | | ... |
| ... | | ... | +---------+
+----------+ +------------+
ここで結合を検討する必要があります。より自然なクエリであることに加えて、ほとんどのデータベースシステム(MySQLを含む)は、多くの賢い人々が多くのハードワークをそのようにクエリの最適化に費やしています。個別のクエリの場合、各クエリは前のクエリの結果に依存するため、クエリを並行して実行することはできず、合計時間はクエリの実際の実行時間だけでなく、結果のフェッチ、シフト、次のクエリのIDのためにそれらを介して、行を一緒にリンクするなど。
以下は100の便利なクエリのリンクです。これらはOracleデータベースでテストされていますが、SQLは標準であり、Oracle、MS SQL Server、MySQL、その他のデータベースの違いはSQL方言です。
バイナリの答えがないことを意味するいくつかの要因があります。パフォーマンスに最適なものは、環境によって異なります。ちなみに、識別子を使用した単一選択が1秒未満でない場合は、構成に問題がある可能性があります。
本当の質問は、データにどのようにアクセスしたいかです。単一選択は遅延バインディングをサポートします。たとえば、従業員情報のみが必要な場合は、従業員テーブルから選択できます。外部キーの関係を使用して、後で必要に応じて関連リソースを取得できます。選択にはすでにポイントするキーがあるので、選択は非常に高速になり、必要なものを取得するだけで済みます。ネットワーク遅延は常に考慮に入れられなければなりません。
結合は、すべてのデータを一度に取得します。レポートを生成したり、グリッドにデータを入力したりする場合は、これで十分です。コンパイルおよびオプトマイズされた結合は、このシナリオでは単一選択よりも高速になります。アドホック結合はそれほど高速ではない可能性があることに注意してください。それらを(ストアドプロシージャに)コンパイルする必要があります。速度の答えは実行計画によって異なります。実行計画では、DBMSがデータを取得するために実行する手順が正確に詳しく説明されています。