案例说明

1554974984600.png
从多个表中获取数据:
1554975020388.png

  1. # 案例:查询员工的姓名及其部门名称
  2. SELECT last_name, department_name
  3. FROM employees, departments;

查询结果:

  1. +-----------+----------------------+
  2. | last_name | department_name |
  3. +-----------+----------------------+
  4. | King | Administration |
  5. | King | Marketing |
  6. | King | Purchasing |
  7. | King | Human Resources |
  8. | King | Shipping |
  9. | King | IT |
  10. | King | Public Relations |
  11. | King | Sales |
  12. | King | Executive |
  13. | King | Finance |
  14. | King | Accounting |
  15. | King | Treasury |
  16. ...
  17. | Gietz | IT Support |
  18. | Gietz | NOC |
  19. | Gietz | IT Helpdesk |
  20. | Gietz | Government Sales |
  21. | Gietz | Retail Sales |
  22. | Gietz | Recruiting |
  23. | Gietz | Payroll |
  24. +-----------+----------------------+
  25. 2889 rows in set (0.01 sec)

分析错误情况:

  1. SELECT COUNT(employee_id) FROM employees;
  2. # 输出 107 行
  3. SELECT COUNT(department_id)FROM departments;
  4. # 输出 27 行
  5. SELECT 107*27 FROM dual;

每个员工都和每个部门匹配了一遍,我们把上述多表查询中出现的问题称为:笛卡尔积的错误。

错误原因:缺少了多表的连接条件

笛卡尔积(或交叉连接)的理解

笛卡尔乘积是一个数学运算。假设我有两个集合 X 和 Y,那么 X 和 Y 的笛卡尔积就是 X 和 Y 的所有可能组合,也就是第一个对象来自于 X,第二个对象来自于 Y 的所有可能。组合的个数即为两个集合中元素个数的乘积数。
一个案例引发的多表连接 - 图3
SQL 92 中,笛卡尔积也称为交叉连接,英文是 CROSS JOIN。在 SQL 99 中也是使用 CROSS JOIN 表示交叉连接。它的作用就是可以把任意表进行连接,即使这两张表不相关。在 MySQL 中如下情况会出现笛卡尔积:

  1. # 查询员工姓名和所在部门名称
  2. SELECT last_name,department_name FROM employees,departments;
  3. SELECT last_name,department_name FROM employees CROSS JOIN departments;
  4. SELECT last_name,department_name FROM employees INNER JOIN departments;
  5. SELECT last_name,department_name FROM employees JOIN departments;

案例分析与问题解决

笛卡尔积的错误会在下面条件下产生

  • 省略多个表的连接条件(或关联条件)
  • 连接条件(或关联条件)无效
  • 所有表中的所有行互相连接

为了避免笛卡尔积, 可以在 WHERE 加入有效的连接条件 加入连接条件后,查询语法:

  1. SELECT table1.column, table2.column
  2. FROM table1, table2
  3. WHERE table1.column1 = table2.column2; # 连接条件

在 WHERE 子句中写入连接条件 正确写法:

  1. # 案例:查询员工的姓名及其部门名称
  2. SELECT last_name, department_name
  3. FROM employees, departments
  4. WHERE employees.department_id = departments.department_id;

在表中有相同列时,在列名之前加上表名前缀。