web-dev-qa-db-ja.com

JOINクエリと複数のクエリ

JOINクエリは複数のクエリよりも高速ですか? (メインクエリを実行し、メインクエリの結果に基づいて他の多くのSELECTを実行します)

それらを結合すると、アプリケーションの設計がかなり複雑になるため、私は尋ねています

それらがより速い場合、だれでもどれくらい大まかに近似できますか? 1.5倍であれば気にしませんが、10倍であれば気にしません。

156
Thomas Bonini

これはあまりにも曖昧すぎて、特定のケースに関連する答えをあなたに与えることはできません。それは多くのことに依存します。ジェフ・アトウッド(このサイトの創設者)は実際に これについて書いた 。ただし、ほとんどの場合、適切なインデックスがあり、JOINを適切に行うと、通常は複数回行うよりも1回行う方が速くなります。

71

内部結合の場合、一致する行のみを取得するため、単一のクエリが意味をなします。左結合の場合、複数のクエリがはるかに優れています。次のベンチマークを見てください。

  1. 5つの結合を持つ単一クエリ

    クエリ:8.074508秒

    結果サイズ:2268000

  2. 5行のクエリ

    複合クエリ時間:.00262秒

    結果サイズ:165​​(6 + 50 + 7 + 12 + 90)

両方のケースで同じ結果が得られることに注意してください(6 x 50 x 7 x 12 x 90 = 2268000)

左結合は、冗長データで指数関数的に多くのメモリを使用します。

2つのテーブルの結合のみを行う場合、メモリ制限はそれほど悪くないかもしれませんが、通常は3つ以上であり、異なるクエリの価値があります。

サイドノートとして、MySQLサーバーはアプリケーションサーバーのすぐそばにあります...接続時間はごくわずかです。接続時間が数秒である場合は、おそらく利点があります

フランク

86
Frank Forte

私は実際に自分で答えを探してこの質問に来ましたが、与えられた答えを読んだ後、考慮すべき変数がたくさんあるため、DBクエリのパフォーマンスを比較する最良の方法は実際の数字を取得することであることに同意するだけですしかし、私はまた、それらの間の数値を比較すると、ほとんどすべての場合に役に立たないと思います。つまり、数値は常に許容可能な数値と比較されるべきであり、互いに比較されるべきではありません。

クエリの1つの方法に0.02秒かかり、もう1つの方法に20秒かかる場合、それは大きな違いです。しかし、クエリの1つの方法に0.0000000002秒かかり、もう1つの方法に0.0000002秒かかる場合はどうでしょうか。どちらの場合でも、1つの方法は他の方法よりも1000倍高速ですが、それは実際にそれでも2番目の場合は「超高速」ですか?

私が個人的に見ているように最終結果:それがうまく機能するなら、簡単な解決策に行きましょう。

20

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を使用して)同様の結果が得られました。

13
levans

個別のクエリと結合の両方を構築し、それぞれの時間を測定します。実際の数値以上に役立つものはありません。

その後、さらに良い-各クエリの先頭に「EXPLAIN」を追加します。これにより、MySQLがデータのリクエストに答えるために使用しているサブクエリの数と、各クエリでスキャンされた行数がわかります。

8

開発者の複雑さと比較したデータベースの複雑さに応じて、多くのSELECT呼び出しを行う方が簡単な場合があります。

JOINと複数のSELECTの両方に対していくつかのデータベース統計を実行してみてください。ご使用の環境で、JOINがSELECTよりも速いか遅いかを確認してください。

繰り返しますが、JOINに変更すると開発作業が1日/週/月余分に行われることになる場合は、複数のSELECTを使い続けます

乾杯、

BLT

7
glasnt

本当の質問は:これらのレコードは1対1の関係または1対多の関係

TLDR回答:

1対1の場合は、JOINステートメントを使用します。

1対多の場合、1つ(または複数)のSELECTステートメントをサーバー側コードの最適化とともに使用します。

最適化のためにSELECTを使用する理由と方法

SELECT 'ingには指数関数的なメモリリークの問題があるため、1対多の関係に基づくレコードの大規模なグループに対するJOIN' ing(結合ではなく複数のクエリ)により最適な効率が得られます。すべてのデータを取得し、サーバー側のスクリプト言語を使用して整理します。

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ステートメントですべてのレコードを取得しています。これは、別のクエリのサブコンポーネントとして一度に1つずつこれらのレコードの小さなグループを取得するJOINよりも優れています。次に、次のようなサーバー側コードで解析します...

<?php
    foreach($addresses as $address) {
         $persons[$address['Personid']]->Address[] = $address;
    }
?>

最適化にJOINを使用しない場合

単一のレコードとの1対1の関係に基づいてJOIN '大規模なレコードグループを作成すると、次のレコードを取得するだけの複数のSELECTステートメントと比較して最適な効率が得られますタイプ。

ただし、1対多の関係を持つレコードを取得する場合、JOINは非効率的です。

例:データベース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

6
HoldOffHunger

この質問は古いですが、いくつかのベンチマークがありません。 JOINを2つの競合他社に対してベンチマークしました。

  • N + 1クエリ
  • 2つのクエリ、WHERE IN(...)または同等のものを使用する2番目のクエリ

結果は明らかです。MySQLでは、JOINmuchより高速です。 N + 1クエリは、アプリケーションのパフォーマンスを大幅に低下させる可能性があります。

JOIN vs WHERE IN vs N+1

つまり、非常に少数の個別の外部レコードを指す多数のレコードを選択しない限りです。極端な場合のベンチマークは次のとおりです。

JOIN vs N+1 - all records pointing to the same foreign record

多対多のリレーションシップに参加している場合を除き、これは通常のアプリケーションでは発生しそうにありません。

取り除く:

  • *対1の関係では、常にJOINを使用します
  • * -to-many関係の場合、2番目のクエリmightの方が高速です

詳細については、 Mediumに関する私の記事 を参照してください。

5
Benjamin

私の経験では、特に大きなデータセットを取得する場合は、通常、複数のクエリを実行する方が速いことがわかりました。

PHPなどの別のアプリケーションからデータベースとやり取りする場合、多くのサーバーへの1回の旅行という議論があります。

サーバーへのトリップ回数を制限し、多くの場合、より高速であるだけでなく、アプリケーションを読みやすくする複数のクエリを実行する他の方法があります-たとえば、mysqli_multi_query。

SQLに関しては私は初心者ではありません。開発者、特に後輩はスマートに見えるので非常に賢い結合を書くのに多くの時間を費やす傾向があると思いますが、実際に見えるデータを抽出するスマートな方法がありますシンプル。

最後の段落は個人的な意見でしたが、これが役立つことを願っています。ベンチマークするべきだと言う人はいますが、他の人にも同意します。どちらのアプローチも特効薬ではありません。

5
A Boy Named Su

スループットの点で高速になりますか?恐らく。しかし、同時にデータベースとスキーマに応じてより多くのデータベースオブジェクトをロックする可能性もあるため、同時実行性が低下します。私の経験では、データベースが同じLAN上にあるほとんどのOLTPシステムで実際にボトルネックがネットワークにあることはほとんどないのに、「データベースの往復回数が少ない」と誤解されることがよくあります。

3
Ramon

以下は100の便利なクエリとのリンクです。これらはOracleデータベースでテストされていますが、SQLは標準であり、Oracle、MS SQL Server、MySQL、その他のデータベースの違いはSQLダイアレクトです。

http://javaforlearn.com/100-sql-queries-learn/

2
S. Mayol

いくつかの要因があります。これは、バイナリの回答がないことを意味します。パフォーマンスに最適なものは、環境によって異なります。ちなみに、識別子を使用した単一選択が1秒未満でない場合、構成に問題がある可能性があります。

尋ねるべき本当の質問は、どのようにデータにアクセスしたいかです。シングルセレクトは遅延バインディングをサポートします。たとえば、従業員情報のみが必要な場合は、従業員テーブルから選択できます。外部キー関係は、後で必要に応じて関連リソースを取得するために使用できます。選択にはすでにポイントするキーがあるため、非常に高速である必要があり、必要なものだけを取得する必要があります。ネットワーク遅延を常に考慮する必要があります。

結合はすべてのデータを一度に取得します。レポートを生成している場合、またはグリッドにデータを入力している場合、これはまさにあなたが望むものです。このシナリオでは、コンパイルおよび最適化された結合は、単一選択よりも単純に高速になります。アドホック結合はそれほど高速ではない可能性があることを忘れないでください。コンパイルして(ストアドプロシージャに)する必要があります。速度の答えは実行計画によって異なります。実行計画は、DBMSがデータを取得するために実行するステップを正確に詳述します。

1
dr.lockett

結合を使用するかどうかは、何よりもまず、結合が意味をなすかどうかについてです。他のほとんどすべての場合、パフォーマンスが大幅に悪化されるため、その時点でのみ、考慮すべきパフォーマンスです。

パフォーマンスの違いは、クエリ対象の情報がどの程度関連しているかに大きく関係しています。結合は動作し、データが関連している場合に高速で、インデックス付けを正しく行いますが、多くの場合、冗長性があり、結果が必要です。また、データセットが直接関連していない場合、それらを1つのクエリに固定すると、デカルト積(基本的には考えられるすべての行の組み合わせ)と呼ばれるものになります。

多くの場合、これは多対1の関係によって引き起こされます。たとえば、 HoldOffHunger's answer は、投稿、タグ、コメントに対する単一のクエリに言及しています。コメントはタグと同様に投稿に関連していますが、タグはコメントとは無関係です。

+------------+     +---------+     +---------+
|  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、行のリンクなどのためにそれらを介して.

1
cHao

はい、JOINSを使用した1つのクエリの方が高速です。クエリを実行しているテーブルの関係、データセットのサイズ、またはプライマリキーの場所がわからなくても、どれほど高速であるかを言うことはほとんど不可能です。

両方のシナリオをテストしてみてください。そうすれば、確実にわかるでしょう...

0
Mathew