web-dev-qa-db-ja.com

複数の結合のバリエーションについて

同じ結果を生成する複数の結合の2つのバージョンがあります。 5つのテーブルは次のとおりです。

customers ← sales ← saleitems → paintings → artists

矢印は(うまくいけば)テーブル間の関係を示しています。

各テーブルにはidという主キーがあり、内部テーブルには[othertable]idという別のテーブルへの外部キーがあります。

最初のバージョンは、上記の順序でリストされたテーブルとのクラシック結合です

SELECT
    c.id, c.givenname, c.familyname,
    a.givenname, a.familyname
FROM
    customers c
        JOIN sales s ON c.id=s.customerid
            JOIN saleitems si ON s.id=si.saleid
                JOIN paintings p ON si.paintingid=p.id
                    JOIN artists a ON p.artistid=a.id

;

2番目のバージョンは、すべてのJOIN句を最初にjaし、ON句を逆順にします。

SELECT
    c.id, c.givenname, c.familyname,
    a.givenname, a.familyname
FROM
    customers c
        JOIN sales s 
            JOIN saleitems si
                JOIN paintings p
                    JOIN artists a
    ON p.artistid=a.id
    ON si.paintingid=p.id
    ON s.id=si.saleid
    ON c.id=s.customerid

;

わかりました。動作しますが、2番目のバージョンの動作を説明できる人はいますか? ON句の順序をJOIN句の逆にする必要があるのはなぜですか? JOINまたはON句をランダムに並べることはできますか?

Microsoft SQLとPostgreSQLでこれをテストしました。

1
Manngo

それらをランダム化することはできません。順序は重要です。結合の間にいくつかの括弧を追加すると、より明確に見えるかもしれません。

SELECT
    c.id
FROM
    customers c
        JOIN (sales s JOIN saleitems si ON s.id = si.saleid)
    ON c.id = s.customerid

このクエリが実行していることは、最初にsalessaleitemsに結合し、次にその結果をcustomersに結合しています。括弧結合内のcustomers列はアクセスできないため参照できません。

SELECT
    c.id
FROM
    customers c
        JOIN (sales s JOIN saleitems si ON s.id = si.saleid 
            AND c.id = c.customerid) -- What is c.customerid here??

メッセージ4104、レベル16、状態1、行6マルチパート識別子「s.customerid」をバインドできませんでした。

あなたが私に尋ねるなら、私はクエリを書く最初の方法にこだわるでしょう、それはほとんどのプログラマにとってその方法ではるかに明確です。

2
EzLo

ほとんどの場合、JOIN構文では、JOINとONの両方が存在する必要があります(ONが違いを生じさせないCROSS JOINなどの特定の状況を除く)。これをBEGIN..ENDステートメント-ENDステートメントでなければなりません。前の最も近いBEGINに関連しています

BEGIN -- block 1
  BEGIN -- block 2
    -- 
  END -- end block 2
END -- end block 1

そのため、ON句を逆の順序で配置すると機能しました。ただし、どの条件がどのテーブルに関連しているかを判断するのに時間がかかるため、これは良い方法ではありません。特に途中でインデント/フォーマットを緩めた場合。

さらに、2番目と次のすべてのJOINSの追加のインデントは不要です。通常、JOIN句の順序は重要ではありません(オプティマイザに強制するクエリヒントを使用しない限り)。

私は個人的には、以下のようにすっきりすると思います(+ INNER JOINを明示的に示し、+関心のある「ファクト」テーブルから結合をアレンジします。他のテーブルは、取得しているファクトの説明です。重要であればオプションで、各列を別々の行に配置します-重要でない場合、通常は1行に配置します)。しかし、再び、これは個別のコーディングスタイルのことです。

SELECT
  c.id, c.givenname, c.familyname,
  a.givenname, a.familyname
FROM
  saleitems si
  INNER JOIN sales s ON si.SaleId = s.Id
  INNER JOIN customers c ON s.CustomerId = c.Id
  INNER JOIN paintings p ON si.PaintingId = p.Id
  INNER JOIN artists a ON p.ArtistId = a.Id
1