理解联接概念和语法

已完成

合并多个表中的数据的最基本且最常用的方法是使用 JOIN 运算。 有些人将 JOIN 视为 SELECT 语句中的单独子句,而其他人则将其视为 FROM 子句的一部分。 本模块主要将其视为 FROM 子句的一部分。 在本模块中,我们将了解 T-SQL SELECT 语句中的 FROM 子句如何创建中间虚拟表,这些虚拟表将在查询的后续阶段使用。

FROM 子句和虚拟表

如果你已了解 SQL Server 处理查询时执行的运算的逻辑顺序,则会发现 SELECT 语句的 FROM 子句是第一个要处理的子句。 此子句确定哪个表或哪些表将成为查询的行源。 FROM 可以引用单个表或将多个表组合在一起作为查询的数据源。 可以将 FROM 子句视为创建和填充一个虚拟表。 该虚拟表将保存 FROM 子句的输出,并由稍后应用的 SELECT 语句的子句使用,例如 WHERE 子句。 在向 FROM 子句添加额外的功能(例如联接运算符)时,可以将 FROM 子句元素的目的理解为在虚拟表中添加或删除行。

由 FROM 子句创建的虚拟表只是逻辑实体。 在 SQL Server 中,不会创建任何物理表(无论是永久的还是临时的)来保存 FROM 子句的结果,因为该表将传递给 WHERE 子句或查询的其他部分。

由 FROM 子句创建的虚拟表包含来自所有联接表的数据。 可以将结果视为集,并将联接结果概念化为维恩图

A Venn diagram showing the set of an Employee table joined to a SalesOrder table

在 T-SQL 语言的整个历史中,它经过不断扩展,反映了 SQL 语言的美国国家标准协会 (ANSI) 标准的更改。 体现这些更改的最明显的地方之一是 FROM 子句中的联接语法。 在 ANSI SQL-89 标准中,通过在以逗号分隔的列表中的 FROM 子句中包含多个表来指定联接。 用于确定要包括哪些行的任何筛选均在 WHERE 子句中执行,如下所示:

SELECT p.ProductID, m.Name AS Model, p.Name AS Product
FROM SalesLT.Product AS p, SalesLT.ProductModel AS m
WHERE p.ProductModelID = m.ProductModelID;

SQL Server 仍支持此语法,但是由于表示复杂联接的筛选器很复杂,因此不建议使用。 此外,如果意外省略了 WHERE 子句,则 ANSI SQL-89 样式的联接很容易成为笛卡尔乘积,并返回过多的结果行,从而导致性能问题,并可能生成错误的结果。

在学习如何用 T-SQL 编写多表查询时,重要的是要了解笛卡尔乘积的概念。 在数学中,笛卡尔乘积是两个集合的乘积。 2 个元素的集合和 6 个元素的集合的乘积是一个包含 12 个元素的集合,也就是 6 x 2。 一个集合中的每个元素都与另一个集合中的每个元素结合在一起。 在下面的示例中,我们有一个包含 2 个元素的名字集合和一个包含 3 个元素的产品集合。 笛卡尔乘积将每个名字与每个产品结合在一起,从而得到 6 个元素。

Cartesian product

在数据库中,笛卡尔乘积是将一个表中的每一行与另一个表中的每一行相结合的结果。 一个包含 10 行的表和一个包含 100 行的表的乘积是一个包含 1,000 行的结果集。 JOIN 运算的基本结果是笛卡尔乘积,但是对于大多数 T-SQL 查询来说,笛卡尔乘积并不是期望的结果。 在 T-SQL 中,当联接两个输入表,而不考虑它们之间的任何关系时,就会产生笛卡尔乘积。 如果没有关于关系的信息,SQL Server 查询处理器将返回所有可能的行组合。 尽管此结果可能有一定的实际应用价值,例如生成测试数据,但通常并没有用,并且可能会对性能产生严重影响。

随着 ANSI SQL-92 标准的出现,添加了对关键字 JOIN 和 ON 子句的支持。 T-SQL 也支持此语法。 在 FROM 子句中通过使用相应的 JOIN 运算符来表示联接。 在 ON 子句中指定了表之间的逻辑关系,该关系成为筛选谓词。

下面的示例使用较新的语法重述了前面的查询:

SELECT p.ProductID, m.Name AS Model, p.Name AS Product
FROM SalesLT.Product AS p
JOIN SalesLT.ProductModel AS m
    ON p.ProductModelID = m.ProductModelID;

注意

ANSI SQL-92 语法使创建偶然的笛卡尔乘积变得更加困难。 添加关键字 JOIN 后,除非将 JOIN 指定为 CROSS JOIN,否则在缺少 ON 子句的情况下将引发语法错误。