sql - 了解涉及 3 个或更多表时 JOIN 的工作原理。 [SQL]

标签 sql join

我想知道是否有人可以帮助我提高对 SQL 中 JOIN 的理解。 [如果它对问题很重要,我会特别考虑 MS SQL Server。]

取 3 个表 A、B [A 通过某些 A.AId 与 B 相关] 和 C [B 通过某些 B.BId 与 C 相关]

如果我编写一个查询,例如

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 连接的结果?

[这意味着根据 A,B 的连接条件,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 带来期末余额代币 - 我最终在结果中得到重复的结果。

[我可以用 DISTINCT 来修复 - 但我试图理解为什么会发生这种情况]

我被告知问题是因为 Balance 和 BalanceToken 之间的关系是 1:M - 当我引入最后一个 JOIN 时,我得到了重复项,因为第三个 JOIN 已经多次引入 BalanceIds 到 (我假设)临时结果集。

我知道示例表不符合良好的数据库设计

对这篇文章表示歉意,感谢您的启发:)

编辑回答 Marc 的问题

从概念上讲,对于一个帐户来说,帐户的 BalanceToken 中不应该有重复项(每个 AccountingDate) - 我认为问题的出现是因为 1 个 Account/AccountingDates 的期末余额是第二天的帐户期初余额 - 所以当 self 加入到多次使用 Balance、BalanceToken 来获取期初和期末余额,我认为余额(BalanceId)被多次纳入“结果组合”中。如果有助于澄清第二个示例,请将其视为每日对账 - 因此左连接 - 可能尚未计算给定帐户/会计日期组合的期初(和/或)期末余额。

最佳答案

概念上是将三个表连接在一起时会发生的情况。

  1. 优化器会提出一个计划,其中包括连接顺序。可以是 A、B、C 或 C、B、A 或任意组合
  2. 查询执行引擎将任何谓词(WHERE 子句)应用于不涉及任何其他表的第一个表。它选择 JOIN 条件或 SELECT 列表或 ORDER BY 列表中提到的列。将此结果称为 A
  3. 它将此结果集连接到第二个表。对于每一行,它连接到第二个表,应用可能适用于第二个表的任何谓词。这会产生另一个临时结果集。
  4. 然后加入最终表并应用 ORDER BY

这就是概念上所发生的情况。事实上,在此过程中有许多可能的优化。关系模型的优点在于,良好的数学基础使得计划的各种变换成为可能,同时又不改变正确性。

例如,实际上没有必要一路生成完整的结果集。 ORDER BY 可以通过首先使用索引访问数据来完成。还可以执行多种类型的连接。

关于sql - 了解涉及 3 个或更多表时 JOIN 的工作原理。 [SQL],我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1083676/

相关文章:

php - 将列更改为 Auto_Increment

mysql - MariaDB/PostgreSQL - 查询优化

php - 从 ROW 获取 ID 并包含在可链接结果中 - PHP MYSQL

sql - SQL Server 2012 Express 中的另一个 LEFT OUTER JOIN 失败

mysql - 查询期间的空值

mysql - 从 SQL 中的 LEFT JOIN 创建矩阵/表

mysql - select 中使用聚合时,where 子句不限制记录

sql - Oracle SQL 从多个元素中提取值

mysql - 在 SQL 中存储数据的正确方法

scala - Apache Flink:使用keyBy/connect维护流中的消息输入顺序