笛卡尔乘积 :

当一个连接条件无效或被遗漏时,其结果是一个笛卡尔乘积 (Cartesian product),其中所有行的组合都被显示。第一个表中的所有行连接到第二个表中的所有行。 
	一个笛卡尔乘积会产生大量的行,其结果没有什么用。你应该在 WHERE 子句中始终包含一个有效的连接条件,除非你有特殊的需求,需要从所有表中组合所有的行。 
	对于一些测试笛卡尔乘积是有用的,例如你需要产生大量的行来模拟一个相当大的数据量。

多表查询(Displaying Data from Multiple Tables):

JOIN:

       SQL 的连接(JOIN)语句将数据库中的两个或多个表组合起来,由"连接"生成的集合, 可以被保存为表, 或者当成表来使用. JOIN 语句的含义是把两张表的属性通过它们的值组合在一起.

基于 ANSI 标准的 SQL 列出了四种 JOIN 方式: 内连接(INNER), 外连接(OUTER), 左外连接(LEFT), 右外连接(RIGHT).在特定的情况下, 一张表(基本表, 视图, 或连接表)可以和自身进行连接, 成为自连接(self-join).

内连接(inner join):

    一般都是默认的连接类型. 将两张表(如 A 和 B)的列组合在一起, 产生新的结果表. 查询会将 A 表的每一行和 B 表的每一行进行比较, 并找出满足连接词的组合. 当连接词被满足, A 和 B 中匹配的行会按列组合(并排组合)成结果集中的一行. 连接产生的结果集, 可以定义为首先对两张表做笛卡尔积(交叉连接) -- 将 A 中的每一行和 B 中的每一行组合, 然后返回满足连接谓词的记录. 实际上 SQL 产品会尽可能用其他方式去实现连接, 笛卡尔积运算是非常没效率的,可以进一步被分为: 相等连接, 自然连接, 和交叉连接.

 

SQL 定义了两种不同语法方式去表示"连接". 首先是"显示连接符号", 它显式地使用关键字 JOIN, 其次是"隐式连接符号". 隐式连接符号把需要连接的表放到 SELECT 语句的 FROM 部分, 并用逗号隔开. 这样就构成了一个"交叉连接", WHERE 语句可能放置一些过滤谓词(过滤条件). 那些过滤谓词在功能上等价于显式连接符号.

 例如:

        下面的查询通过 Employee 表和 Department 表共有的属性 DepartmentID 连接两表. 在两表 DepartmentID 匹配之处(如连接谓词被满足), 查询将组合两表的 LastName, DepartmentID 和DepartmentName 等列, 把它们放到结果表的一行(一条记录)里. 当 DepartmentID 不匹配, 就不会往结果表中生成任何数据.

 

--显式的内连接实例:
	select e.EmployeeId,e.LastName,d.部門,d.DepartmentId FROM Employee as e INNER JOIN Department as d ON e.DepartmentID = d.DepartmentId

 等价于:

select e.EmployeeId,e.LastName,d.部門,d.DepartmentId FROM Employee as e,Department as d where e.DepartmentID = d.DepartmentId

是比较连接的一种特例, 它的连接词只用了相等比较. 使用其他比较操作符(如 <)的不是相等连接.

实例:

--SQL 提供了一种可选的简短符号去表达相等连接, 它使用 USING 关键字:
	select e.EmployeeId,e.LastName,d.部門,d.DepartmentId FROM Employee as e INNER JOIN Department as d USING(DepartmentId)

     USING 结构并不仅仅是语法糖(指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。), 上面查询的结果和使用显式谓词得到的查询得到的结果是不同的. 特别地, 在 USING 部分列出的列(column)将以只出现一次, 且名称无表名修饰.在上面的例子中, 将产生单独的名为 DepartmentID 的列, 而不是 employee.DepartmentID 或 department.DepartmentID.

自然连接:

--上面用于内连接的查询实例可以用自然连接的方式表示如下:
SELECT * FROM Employee NATURAL JOIN Department

用了 USING 语句后, 在连接表中, DepartmentID 列只出现一次, 且没有表名作前缀:

      在 Oracle 里用 JOIN USING 或 NATURAL JOIN 时, 如果两表共有的列的名称前加上某表名作为前缀, 则会报编译错误: "ORA-25154: column part of USING clause cannot have qualifier" 或 "ORA-25155: column used in NATURAL join cannot have qualifier".

交叉连接(cross join):

    用于交叉连接的 SQL 代码在 FROM 列出表名, 但并不包含任何过滤的连接谓词.

--显式的交叉连接实例:
		SELECT * FROM   Employee CROSS JOIN Department
	--隐式的交叉连接实例:
		SELECT * FROM   Employee, Department

 外连接:

    外连接并不要求连接的两表的每一条记录在对方表中都一条匹配的记录. 连接表保留所有记录 -- 甚至这条记录没有匹配的记录也要保留. 外连接可依据连接表保留左表, 右表或全部表的行而进一步分为左外连接, 右外连接和全连接.(在这种情况下left<左> 和 right<右> 表示 JOIN 关键字的两边.)在标准的 SQL 语言中, 外连接没有隐式的连接符号.

左外连接:

    左外连接(left outer join), 亦简称为左连接(left join), 若 A 和 B 两表进行左外连接, 那么结果表中将包含"左表"(即表 A)的所有记录, 即使那些记录在"右表" B 没有符合连接条件的匹配. 这意味着即使 ON 语句在 B 中的匹配项是0条, 连接操作还是会返回一条记录, 只不过这条记录的中来自于 B 的每一列的值都为 NULL. 这意味着左外连接会返回左表的所有记录和右表中匹配记录的组合(如果右表中无匹配记录, 来自于右表的所有列的值设为 NULL). 如果左表的一行在右表中存在多个匹配行, 那么左表的行会复制和右表匹配行一样的数量, 并进行组合生成连接结果.

   如, 这允许我们去找到雇员的部门时, 显示所有雇员, 即使这个雇员还没有关联的部门. (在上面的内连接部分由一个相反的例子, 没有关联的部门号的雇员在结果中是不显示的).

--左外连接实例: (相对于内连接增添的行用斜体标出)
	SELECT *  FROM   Employee  LEFT OUTER JOIN Department  ON Employee.DepartmentID = Department.DepartmentId

 

右外连接:

       右外连接, 亦简称右连接, 它与左外连接完全类似, 只不过是作连接的表的顺序相反而已. 如果 A 表右连接 B 表, 那么"右表" B 中的每一行在连接表中至少会出现一次. 如果 B 表的记录在"左表" A 中未找到匹配行, 连接表中来源于 A 的列的值设为 NULL.右连接操作返回右表的所有行和这些行在左表中匹配的行(没有匹配的, 来源于左表的列值设为 NULL).

      例如, 这允许我们在找每一个雇员以及他的部门信息时, 当这个部门里没有任何雇员时, 也把部分显示出来.

--右连接的实例: (相对于内连接增添的行用斜体标出)
SELECT * FROM   Employee RIGHT OUTER JOIN Department ON Employee.DepartmentID = Department.DepartmentID
--实际上显式的右连接很少使用, 因为它总是可以被替换成左连接--换换表的位置就可以了, 另外, 右连接相对于左连接并没有什么额外的功能. 上表同样可以使用左连接得到:
SELECT * FROM   Department LEFT OUTER JOIN Employee ON Employee.DepartmentID = Department.DepartmentID

全连接:

    全连接是左右外连接的并集. 连接表包含被连接的表的所有记录, 如果缺少匹配的记录, 即以 NULL 填充.如, 这允许我们查看每一个在部门里的员工和每一个拥有雇员的部门, 同时, 还能看到不在任何部门的员工以及没有任何员工的部门.

    全连接实例:

SELECT *  FROM   Employee  FULL OUTER JOIN Department ON Employee.DepartmentID = Department.DepartmentID
--一些数据库系统(如 MySQL)并不直接支持全连接, 但它们可以通过左右外连接的并集(参: union)来模拟实现. 和上面等价的实例:

SELECT *
FROM   Employee 
       LEFT JOIN Department 
          ON Employee.DepartmentID = Department.DepartmentID

SELECT *
FROM   Employee
       RIGHT JOIN Department
          ON Employee.DepartmentID = Department.DepartmentID
WHERE  Employee.DepartmentID IS NULL

 自连接:

    自连接就是和自身连接;

    构建一个查询, 它试图找到这样的记录: 每条记录包含两个雇员, 他们来自于同一个国家. 如果你有两张雇员表(Employee), 那么只要第一张表的雇员和第二张表的雇员在同样的国家的就行了, 你可以用一个通常的连接(相等连接)操作去得到这个表. 不过, 这里所有雇员信息都在一张但对大表里.

SELECT 
e.EmployeeId,
e.LastName,
e.DepartmentID,
e.Country,
f.EmployeeId,
f.LastName,
f.DepartmentID,
f.Country
FROM Employee f,Employee e
WHERE f.Country = e.Country
AND e.EmployeeId<f.EmployeeId
ORDER BY e.EmployeeId,f.EmployeeId;

 关于这个例子, 请注意:

    * F 和 S 是雇员表(employee)的第一个和第二个拷贝的别名
    * 条件 F.Country = S.Country 排除了在不同国家的雇员的组合. 这个例子仅仅期望得到在相同国家的雇员的组合.
    * 条件 F.EmployeeID < S.EmployeeID 排除了雇员号(EmployeeID)相同的组合.
    * F.EmployeeID < S.EmployeeID 排除了重复的组合. 没有这个条件的话, 将生成类似下面表中的无用数据

 

替代方式
    外连接查询得到的结果也可以通过关联子查询得到. 例如:

SELECT 
e.LastName, 
e.DepartmentID,
Department.部門
FROM  Employee e 
LEFT OUTER JOIN Department
ON e.DepartmentID = Department.DepartmentId

 还可以这样:

SELECT 
e.LastName, 
e.DepartmentID,
(SELECT Department.部門 FROM Department
WHERE e.DepartmentID = Department.DepartmentId)
FROM  Employee e