web-dev-qa-db-ja.com

MySQL JOINはどのような順序で評価されますか?

私は次のクエリを持っています:

SELECT c.*
FROM companies AS c
JOIN users AS u USING(companyid)
JOIN jobs AS j USING(userid)
JOIN useraccounts AS us USING(userid)
WHERE j.jobid = 123;

次の質問があります。

  1. USING構文はON構文と同義ですか?
  2. これらの結合は左から右に評価されますか?言い換えると、このクエリは次のようになりますか。x=企業がユーザーに参加します。 y = xJOINジョブ; z = yJOINユーザーアカウント;
  3. 質問2の答えが「はい」の場合、companysテーブルにcompanyid、userid、およびjobid列があると想定しても安全ですか?
  4. エイリアス「j」を参照しているときに、WHERE句を使用してcompanysテーブルの行を選択する方法がわかりません。

どんな助けでもいただければ幸いです!

26
Kyle Noland
  1. USING(フィールド名)は、ON table1.fieldname = table2.fieldnameを表す簡単な方法です。

  2. SQLは、言語の性質ではないため、JOINSが実行される「順序」を定義しません。明らかに、ステートメントで順序を指定する必要がありますが、INNER JOINは可換であると見なすことができます。それらを任意の順序でリストすると、同じ結果が得られます。

    そうは言っても、SELECT ... JOIN、特にLEFT JOINを含むものを作成する場合、3番目のJOINを最初のJOINの結果に新しいテーブルを結合するものと見なし、4番目のJOINを2番目のJOINの結果など。

    ごくまれに、指定された順序がヒューリスティックに影響を与える方法のために、クエリオプティマイザの動作に影響を与える可能性があります。

  3. いいえ。クエリの組み立て方法では、会社とユーザーの両方に会社IDがあり、ジョブにはユーザーIDとジョブIDがあり、ユーザーアカウントにはユーザーIDが必要です。ただし、JOINが機能するために必要なのは、企業の1つだけですまたはユーザー。

  4. WHERE句は、jobsテーブルによって提供される列を使用して、結果全体(つまり、すべてのJOINされた列)をフィルタリングします。

26
staticsan

USING構文については少し答えられません。それは変だ。私はこれまで見たことがなく、代わりに常にON句を使用していました。

しかし、私ができることは、JOIN操作の順序は、最適化ヒューリスティックのシステムに基づいて、クエリプランを構築するときに、クエリオプティマイザによって動的に決定されることです。は:

  1. JOINは主キーフィールドで実行されますか?その場合、これはクエリプランで高い優先度を取得します。

  2. JOINは外部キーフィールドで実行されますか?これも優先度が高くなります。

  3. 結合されたフィールドにインデックスが存在しますか?もしそうなら、優先順位を上げます。

  4. WHERE句のフィールドでJOIN操作が実行されますか? WHERE句の式は、(テーブルスキャンを実行するのではなく)インデックスを調べることで評価できますか?これは主要な最適化の機会であるため、主要な優先順位が上がります。

  5. 結合された列のカーディナリティは何ですか?カーディナリティの高い列は、オプティマイザに誤った一致(WHERE句またはON句を満たさないもの)を区別する機会を増やすため、カーディナリティの高い結合は通常、カーディナリティの低い結合の前に処理されます。

  6. 結合されたテーブルには実際の行がいくつありますか?値が100しかないテーブルに対して結合すると、1,000万行のテーブルに対して結合するよりもデータの急増が少なくなります。

とにかく...要点は...クエリ実行プランに入る変数がたくさんあります。 MySQLがクエリを最適化する方法を確認したい場合は、EXPLAIN構文を使用してください。

そして、ここに読むべき良い記事があります:

http://www.informit.com/articles/article.aspx?p=377652


編集中:

4番目の質問に答えるには:「会社」テーブルにクエリを実行していません。 FROM句とUSING句の[〜#〜] all [〜#〜]4つのテーブルの結合された外積をクエリしています。

「j.jobid」エイリアスは、結合されたテーブルのコレクション内の列の1つの完全修飾名です。

11
benjismith

MySQLでは、クエリオプティマイザに何をする予定かを尋ねるのは興味深いことがよくあります。

EXPLAIN SELECT [...]

"7.2.1 EXPLAINを使用したクエリの最適化" を参照してください。

3
NickZoic

JOINの優先順位に関するより詳細な回答は次のとおりです。あなたの場合、JOINsはすべて可換です。そうでないところを試してみましょう。

スキーマの構築:

CREATE TABLE users (
  name text
);

CREATE TABLE orders (
  order_id text,
  user_name text
);

CREATE TABLE shipments (
  order_id text,
  fulfiller text
);

データの追加:

INSERT INTO users VALUES ('Bob'), ('Mary');

INSERT INTO orders VALUES ('order1', 'Bob');

INSERT INTO shipments VALUES ('order1', 'Fulfilling Mary');

クエリの実行:

SELECT *
  FROM users
       LEFT OUTER JOIN orders
       ON orders.user_name = users.name
       JOIN shipments
       ON shipments.order_id = orders.order_id

結果:

ボブ行のみが返されます

分析:

このクエリでは、最初にLEFT OUTER JOINが評価され、LEFT OUTER JOINの複合結果に対してJOINが評価されました。

2番目のクエリ:

SELECT *
  FROM users
       LEFT OUTER JOIN (
         orders
         JOIN shipments
         ON shipments.order_id = orders.order_id)
         ON orders.user_name = users.name

結果:

ボブ(フルフィルメントデータを含む)の1行と、フルフィルメントデータのNULLを含むメアリーの1行。

分析:

括弧は評価順序を変更しました。


その他のMySQLドキュメントは https://dev.mysql.com/doc/refman/5.5/en/nested-join-optimization.html にあります。

0

参照 http://dev.mysql.com/doc/refman/5.0/en/join.html

そしてここから読み始めてください:


MySQL 5.0.12での処理の変更に参加する

MySQL 5.0.12以降、自然結合およびUSINGを使用した結合(外部結合バリアントを含む)は、SQL:2003標準に従って処理されます。目標は、SQL:2003に従って、NATURALJOINとJOIN ... USINGに関してMySQLの構文とセマンティクスを調整することでした。ただし、結合処理におけるこれらの変更により、一部の結合の出力列が異なる可能性があります。また、古いバージョンで正しく機能するように見えた一部のクエリは、標準に準拠するように書き直す必要があります。

これらの変更には、5つの主要な側面があります。

  • MySQLがNATURALまたはUSING結合操作の結果列(したがって、FROM句全体の結果)を決定する方法。

  • SELECT *およびSELECTtbl_name。*を選択された列のリストに展開します。

  • NATURALまたはUSING結合の列名の解決。

  • NATURALまたはUSINGの変換は、JOIN ... ONに結合します。

  • JOIN ... ONのON状態での列名の解決。

0
micahwittman

ONとUSINGの部分についてはよくわかりません(ただし、これは website は同じだと言っています)

順序付けの質問に関しては、その完全な実装(およびおそらくクエリ)固有です。 MYSQLは、リクエストをコンパイルするときに注文を選択する可能性があります。特定の順序を適用したい場合は、クエリを「ネスト」する必要があります。

SELECT c.*
FROM companies AS c 
    JOIN (SELECT * FROM users AS u 
        JOIN (SELECT * FROM  jobs AS j USING(userid) 
              JOIN useraccounts AS us USING(userid) 
              WHERE j.jobid = 123)
    )

パート4の場合と同様に、where句は、ジョブテーブルのどの行に参加できるかを制限します。したがって、ユーザーIDが一致しているために結合する行があるが、正しいジョブIDがない場合、それらは省略されます。

0
luke

1)使用方法は、onとまったく同じではありませんが、両方のテーブルに、結合する同じ名前の列がある略記です...を参照してください: http://www.Java2s.com/Tutorial/MySQL/0100__Table-Join/ThekeywordUSINGは、tableJoins.htmの間にONキーワードの代わりに使用できます

私の意見では読みにくいので、結合を詳しく説明します。

3)このクエリからは明らかではありませんが、そうではないと思います。

2)他のテーブル(すべてが直接会社にあるわけではありません)を介して結合していると仮定すると、このクエリの順序は重要です...以下の比較を参照してください。

オリジナル:

SELECT c.* 
    FROM companies AS c 
    JOIN users AS u USING(companyid) 
    JOIN jobs AS j USING(userid) 
    JOIN useraccounts AS us USING(userid) 
WHERE j.jobid = 123

私が示唆していると思うこと:

SELECT c.* 
    FROM companies AS c 
    JOIN users AS u on u.companyid = c.companyid
    JOIN jobs AS j on j.userid = u.userid
    JOIN useraccounts AS us on us.userid = u.userid 
WHERE j.jobid = 123

ここで、ジョブとユーザーアカウントに参加するラインを切り替えることができます。

すべてが会社に参加した場合はどうなるか:

SELECT c.* 
    FROM companies AS c 
    JOIN users AS u on u.companyid = c.companyid
    JOIN jobs AS j on j.userid = c.userid
    JOIN useraccounts AS us on us.userid = c.userid
WHERE j.jobid = 123

これは実際には論理的に意味がありません...各ユーザーが独自の会社を持っていない限り。

4.)SQLの魔法は、特定の列しか表示できないことですが、それらはすべて並べ替えとフィルタリング用です...

あなたが戻った場合

SELECT c.*, j.jobid....  

何がフィルタリングされているかを明確に確認できますが、データベースサーバーは、フィルタリングのために行を出力するかどうかを気にしません。

0
Dave K