3つ以上のテーブルが関係する場合のJOINの動作を理解する。[SQL]
質問
SQL の JOIN に関する私の理解を深めるために、誰かが手助けしてくれないかと思います。[それが問題に重要である場合、私は特にMS SQL Serverを考えています]。
3つのテーブルA、B [いくつかのA.AIdによってBに関連するA]、およびC [いくつかのB.BIdによってCに関連するB]を取る。
もし私が以下のようなクエリを構成するとしたら
SELECT *
FROM A JOIN B
ON A.AId = B.AId
すべて順調です - これがどのように機能するのか、私は気に入っています。
テーブルC(または他のD、E、......)が追加されたらどうなるのでしょうか?
このような状況で
SELECT *
FROM A JOIN B
ON A.AId = B.AId
JOIN C ON C.BId = B.BId
Cは何に結合しているのでしょうか?- Bテーブル(とその中の値)でしょうか? それとも、Cテーブルが結合しているA+B結合の結果である、他の一時的な結果セットですか?
[Bテーブルにあるすべての値が、A,Bの結合条件に基づく一時的な結果セットA+Bにあるとは限らないという意味です]。
私がなぜ質問しているのかの具体的な(そしてかなり作為的な)例は、私が以下の中で見ている動作を理解しようとしているからです。
Tables
Account (AccountId, AccountBalanceDate, OpeningBalanceId, ClosingBalanceId)
Balance (BalanceId)
BalanceToken (BalanceId, TokenAmount)
Where:
Account->Opening, and Closing Balances are NULLABLE
(may have opening balance, closing balance, or none)
Balance->BalanceToken is 1:m - a balance could consist of many tokens
概念的には、ある日の期末残高が明日の期首残高になります。
もし私が、ある口座のすべての開始残高と終了残高のリストを見つけようとした場合
のようなことをするかもしれません。
SELECT AccountId
, AccountBalanceDate
, Sum (openingBalanceAmounts.TokenAmount) AS OpeningBalance
, Sum (closingBalanceAmounts.TokenAmount) AS ClosingBalance
FROM Account A
LEFT JOIN BALANCE OpeningBal
ON A.OpeningBalanceId = OpeningBal.BalanceId
LEFT JOIN BALANCE ClosingBal
ON A.ClosingBalanceId = ClosingBal.BalanceId
LEFT JOIN BalanceToken openingBalanceAmounts
ON openingBalanceAmounts.BalanceId = OpeningBal.BalanceId
LEFT JOIN BalanceToken closingBalanceAmounts
ON closingBalanceAmounts.BalanceId = ClosingBal.BalanceId
GROUP BY AccountId, AccountBalanceDate
最後のJOINがclosing balance tokensをもたらすまで、物事は私が期待するように動作します - ここで私は結果に重複をもたらすことになります。
[DISTINCTで修正することができますが、なぜこのようなことが起こっているのかを理解しようとしています。]
問題は、Balance と BalanceToken の間の関係が 1:M であるためであり、最後の JOIN を持ち込むと、3番目の JOIN がすでに BalanceIds を複数回 (おそらく) 一時結果セットに持ち込んだため、重複が発生すると言われています。
私は、例のテーブルが良い DB 設計に適合していないことを知っています。
エッセイのために謝罪し、任意の洞察に感謝します:)
Marcの質問に答える形で編集
概念的には、1つのアカウントに対して(AccountingDateごとに)BalanceTokenに重複があるべきではありません - 1つのアカウント / AccountingDatesの終値が翌日のそのアカウントの開始残高になるため、問題が発生すると思います - したがって、開始および終了残高を得るために何度もBalance、BalanceTokenに自己結合すると、残高(BalanceId)が「結果の混合物」に複数回持ち込まれていると考えられます。2番目の例を明確にするのに役立つのであれば、毎日の照合と考え、それゆえ左結合を行います - 開始(および/または)終了バランスは、与えられたアカウント/会計日の組み合わせに対して計算されていないかもしれません。
どのように解決するのですか?
概念的に は、3つのテーブルを結合するとどうなるかを示しています。
- オプティマイザは結合順を含む計画を作成します。 A、B、C、またはC、B、A、またはその組み合わせのいずれかになります。
-
クエリ実行エンジンは任意の述語 (
WHERE
節)を適用します。 それは、述語の中にあるJOIN
条件またはSELECT
のリストかORDER BY
のリストと同じです。 この結果を A と呼びます。 - この結果集合を2番目のテーブルに結合する。 各行に対して、2番目のテーブルに適用されるかもしれないあらゆる述語を適用して、2番目のテーブルに結合する。 この結果、別の一時的な結果集合ができる。
-
そして、最後のテーブルに結合し、述語を適用します。
ORDER BY
これは概念的に起こることです。 実際には、途中で多くの可能な最適化があります。リレーショナルモデルの利点は、健全な数学的基礎があるため、正しさを変えずに計画のさまざまな変換が可能であることです。
例えば、途中で完全な結果セットを生成する必要は本当にありません。 そのため
ORDER BY
はインデックスを使用してデータにアクセスすることによって行われるかもしれません。 同様に、多くの種類の結合を行うことができます。
関連
-
MySQL - ストアドプロシージャ (データ型、関数)
-
[解決済み] Oracle Trigger ORA-04098: トリガーが無効で、再バリデーションに失敗しました。
-
[解決済み] MySQLの「スキーマの作成」と「データベースの作成」 - 違いはあるのか?
-
[解決済み] SQL Server : 列を行に変換する
-
[解決済み] SQLのSELECTでIF...THENを実行するにはどうすればよいですか?
-
[解決済み] SQL ServerでJOINを使用してUPDATE文を実行するにはどうすればよいですか?
-
[解決済み] SQLite - UPSERT *not* INSERT or REPLACE
-
[解決済み] DISTINCTでCOUNT(*)を選択する
-
[解決済み] 3つのテーブルを持つSQL Inner-join?
-
[解決済み] SQL ServerでINNER JOINを使用して削除するにはどうすればよいですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[エラー処理] [MySQL] [Err] 1241 - オペランドは 1 つのカラムを含む必要があります。
-
executeQuery()ソリューションでデータ操作文を発行できない。
-
[解決済み] SQLサーバーでNULL = NULLがfalseに評価される理由
-
[解決済み] ATTACHで開いたSQLiteデータベースファイルのテーブルを一覧表示するにはどうすればよいですか?
-
[解決済み] INNER JOIN ON vs WHERE句
-
[解決済み] SQL JOIN - WHERE句とON句の比較
-
[解決済み] postgres: ユーザーをスーパーユーザーにアップグレードしますか?
-
[解決済み] Entity Framework VS LINQ to SQL VS ADO.NETでストアドプロシージャを使う?[クローズド]
-
[解決済み] SQL Serverで、ある日付より大きいすべての日付を照会するにはどうすればよいですか?
-
[解決済み] VARCHARとCHARの違いは何ですか?