# 2889行结果 +-----------+----------------------+ | last_name | department_name | +-----------+----------------------+ | King | Administration | | King | Marketing | | King | Purchasing | | King | Human Resources | | King | Shipping | | King | IT |
笛卡尔乘积是一个数学运算。假设有两个集合 X 和 Y,那么 X 和 Y 的笛卡尔积就是 X 和 Y 的所有可能组合,也就是第一个对象来自于 X,第二个对象来自于 Y 的所有可能。组合的个数即为两个集合中元素个数的乘积数 SQL92中,笛卡尔积也称为 交叉连接 ,英文是 CROSS JOIN 。在 SQL99 中也是使用 CROSS JOIN表示交叉连接。它的作用就是可以把任意表进行连接,即使这两张表不相关。在MySQL中如下情况会出现笛卡尔积:
1 2 3 4 5
#查询员工姓名和所在部门名称 SELECT last_name,department_name FROM employees,departments; SELECT last_name,department_name FROM employees CROSSJOIN departments; SELECT last_name,department_name FROM employees INNERJOIN departments; SELECT last_name,department_name FROM employees JOIN departments;
笛卡尔积的错误会在下面条件下产生:
省略多个表的连接条件(或关联条件)
连接条件(或关联条件)无效
所有表中的所有行互相连接
为了避免笛卡尔积, 可以在 WHERE 加入有效的连接条件。 加入连接条件后,查询语法:
1 2 3
SELECT table1.column, table2.column FROM table1, table2 WHERE table1.column1 = table2.column2; #连接条件
在 WHERE子句中写入连接条件。 正确写法:
1 2 3 4 5
#案例:查询员工的姓名及其部门名称 SELECT last_name, department_name FROM employees, departments WHERE employees.department_id = departments.department_id; 1234
SELECT employees.employee_id, employees.last_name, employees.department_id, departments.department_id, departments.location_id FROM employees, departments WHERE employees.department_id = departments.department_id;
拓展1:多个连接条件与 AND 操作符 拓展2:区分重复的列名
多个表中有相同列时,必须在列名之前加上表名前缀。
在不同表中具有相同列名的列可以用 表名 加以区分。
1 2 3
SELECT employees.last_name, departments.department_name,employees.department_id FROM employees, departments WHERE employees.department_id = departments.department_id;
拓展3:表的别名
使用别名可以简化查询。
列名前使用表名前缀可以提高查询效率。
1 2 3 4
SELECT e.employee_id, e.last_name, e.department_id, d.department_id, d.location_id FROM employees e , departments d WHERE e.department_id = d.department_id;
需要注意的是,如果使用了表的别名,在查询字段中、过滤条件中就只能使用别名进行代替,不能使用原有的表名,否则就会报错。 阿里开发规范: 【强制 】对于数据库中表记录的查询和变更,只要涉及多个表,都需要在列名前加表的别名(或表名)进行限定。 说明:对多表进行查询记录、更新记录、删除记录时,如果对操作列没有限定表的别名(或表名),并且操作列在多个表中存在时,就会抛异常。 正例 :select t1.name from table_first as t1 , table_second as t2 where t1.id=t2.id; 反例:在某业务中,由于多表关联查询语句没有加表的别名(或表名)的限制,正常运行两年后,最近在 某个表中增加一个同名字段,在预发布环境做数据库变更后,线上查询语句出现出 1052 异常:Column ‘name’ in field list is ambiguous。
SELECT CONCAT(worker.last_name ,' works for ' , manager.last_name) FROM employees worker, employees manager WHERE worker.manager_id = manager.employee_id ; /* +------------------------------------------------------------+ | CONCAT(worker.last_name ,' works for ', manager.last_name) | +------------------------------------------------------------+ | Kochhar works for King | | De Haan works for King | | Hunold works for De Haan | | Ernst works for Hunold | | Austin works for Hunold | | Pataballa works for Hunold | | Lorentz works for Hunold | | Greenberg works for Kochhar | */
#左外连接 SELECT last_name,department_name FROM employees ,departments WHERE employees.department_id = departments.department_id(+); #右外连接 SELECT last_name,department_name FROM employees ,departments WHERE employees.department_id(+) = departments.department_id;
#7.3 内连接 vs 外连接 # 内连接:合并具有同一列的两个以上的表的行, 结果集中不包含一个表与另一个表不匹配的行 SELECT employee_id,department_name FROM employees e,departments d WHERE e.`department_id` = d.department_id; #只有106条记录
#练习:查询所有的员工的last_name,department_name信息 #所有的-->外连接 SELECT employee_id,department_name FROM employees e,departments d WHERE e.`department_id` = d.department_id; # 需要使用左外连接
3. SQL99语法实现多表查询
3.1 基本语法
使用JOIN…ON子句创建连接的语法结构:
1 2 3 4
SELECT table1.column, table2.column,table3.column FROM table1 JOIN table2 ON table1 和 table2 的连接条件 JOIN table3 ON table2 和 table3 的连接条件
它的嵌套逻辑类似我们使用的 FOR 循环:
1 2 3 4 5 6
for t1 in table1: for t2 in table2: if condition1: for t3 in table3: if condition2: output t1 + t2 + t3
#SQL92语法实现内连接:见上,略 #SQL92语法实现外连接:使用 +----------MySQL不支持SQL92语法中外连接的写法! #Mysql不支持,Oracle支持: SELECT employee_id,department_name FROM employees e,departments d WHERE e.`department_id` = d.department_id(+);#编译不过
#SQL99语法实现内连接: SELECT last_name,department_name FROM employees e INNERJOIN departments d ON e.`department_id` = d.`department_id`; #INNER可省略 /*部分输出 +-------------+------------------+ | last_name | department_name | +-------------+------------------+ | Whalen | Administration | | Hartstein | Marketing | | Fay | Marketing | */
#三张表的内连接 SELECT last_name,department_name,city FROM employees e JOIN departments d ON e.`department_id` = d.`department_id` JOIN locations l ON d.`location_id` = l.`location_id`; /*部分输出 +-------------+------------------+---------------------+ | last_name | department_name | city | +-------------+------------------+---------------------+ | Whalen | Administration | Seattle | | Hartstein | Marketing | Toronto | | Fay | Marketing | Toronto | */
#SQL99语法实现外连接: #练习:查询所有的员工的last_name,department_name信息 #左、右外连接OUTER可省略 # 左外连接: SELECT last_name,department_name FROM employees e LEFTJOIN departments d ON e.`department_id` = d.`department_id`;#107条记录
#右外连接: SELECT last_name,department_name FROM employees e RIGHTOUTERJOIN departments d ON e.`department_id` = d.`department_id`;#122条记录
#满外连接:mysql不支持FULLOUTERJOIN,Oracle支持: SELECT last_name,department_name FROM employees e FULLOUTERJOIN departments d ON e.`department_id` = d.`department_id`;
#方式1 SELECT*FROM employees WHERE email LIKE'%a%'OR department_id>90; #方式2 SELECT*FROM employees WHERE email LIKE'%a%' UNION SELECT*FROM employees WHERE department_id>90;
举例:查询中国用户中男性的信息以及美国用户中年男性的用户信息
1 2 3
SELECT id,cname FROM t_chinamale WHERE csex='男' UNIONALL SELECT id,tname FROM t_usmale WHERE tGender='male';
#中图:内连接 A∩B SELECT employee_id,last_name,department_name FROM employees e JOIN departments d ON e.`department_id` = d.`department_id`;
#左上图:左外连接 SELECT employee_id,last_name,department_name FROM employees e LEFTJOIN departments d ON e.`department_id` = d.`department_id`;
#右上图:右外连接 SELECT employee_id,last_name,department_name FROM employees e RIGHTJOIN departments d ON e.`department_id` = d.`department_id`;
#左中图:A - A∩B SELECT employee_id,last_name,department_name FROM employees e LEFTJOIN departments d ON e.`department_id` = d.`department_id` WHERE d.`department_id` ISNULL
#右中图:B-A∩B SELECT employee_id,last_name,department_name FROM employees e RIGHTJOIN departments d ON e.`department_id` = d.`department_id` WHERE e.`department_id` ISNULL
#左下图:满外连接
# 左中图 + 右上图 A∪B
SELECT employee_id,last_name,department_name FROM employees e LEFTJOIN departments d ON e.`department_id` = d.`department_id` WHERE d.`department_id` ISNULL UNIONALL #没有去重操作,效率高 SELECT employee_id,last_name,department_name FROM employees e RIGHTJOIN departments d ON e.`department_id` = d.`department_id`;
#右下图 #左中图 + 右中图 A ∪B- A∩B 或者 (A - A∩B) ∪ (B - A∩B) SELECT employee_id,last_name,department_name FROM employees e LEFTJOIN departments d ON e.`department_id` = d.`department_id` WHERE d.`department_id` ISNULL UNIONALL SELECT employee_id,last_name,department_name FROM employees e RIGHTJOIN departments d ON e.`department_id` = d.`department_id` WHERE e.`department_id` ISNULL
5.7.2 语法格式小结
左中图
1 2 3 4 5
#实现A - A∩B select 字段列表 from A表 leftjoin B表 on 关联条件 where 从表关联字段 isnulland 等其他子句;
右中图
1 2 3 4 5
#实现B - A∩B select 字段列表 from A表 rightjoin B表 on 关联条件 where 从表关联字段 isnulland 等其他子句;
左下图
1 2 3 4 5 6 7 8 9 10 11
#实现查询结果是A∪B #用左外的A,union 右外的B select 字段列表 from A表 leftjoin B表 on 关联条件 where 等其他子句 union select 字段列表 from A表 rightjoin B表 on 关联条件 where 等其他子句;
右下图
1 2 3 4 5 6 7 8 9 10 11
#实现A∪B - A∩B 或 (A - A∩B) ∪ (B - A∩B) #使用左外的 (A - A∩B) union 右外的(B - A∩B) select 字段列表 from A表 leftjoin B表 on 关联条件 where 从表关联字段 isnulland 等其他子句 union select 字段列表 from A表 rightjoin B表 on 关联条件 where 从表关联字段 isnulland 等其他子句
# 中图:内连接 SELECT employee_id,department_name FROM employees e JOIN departments d ON e.`department_id` = d.`department_id`;#106条记录
# 左上图:左外连接 SELECT employee_id,department_name FROM employees e LEFTJOIN departments d ON e.`department_id` = d.`department_id`;#107条记录
# 右上图:右外连接 SELECT employee_id,department_name FROM employees e RIGHTJOIN departments d ON e.`department_id` = d.`department_id`;#122条记录
# 左中图(在左上图中抹去中间部分): SELECT employee_id,department_name FROM employees e LEFTJOIN departments d ON e.`department_id` = d.`department_id` WHERE d.`department_id` ISNULL;#1条记录
# 右中图(在右上图中抹去中间部分): SELECT employee_id,department_name FROM employees e RIGHTJOIN departments d ON e.`department_id` = d.`department_id` WHERE e.`department_id` ISNULL;#16条记录
# 左下图:满外连接 # 方式1:左上图 UNIONALL 右中图
SELECT employee_id,department_name FROM employees e LEFTJOIN departments d ON e.`department_id` = d.`department_id` UNIONALL SELECT employee_id,department_name FROM employees e RIGHTJOIN departments d ON e.`department_id` = d.`department_id` WHERE e.`department_id` ISNULL;#123条记录
# 方式2:左中图 UNIONALL 右上图
SELECT employee_id,department_name FROM employees e LEFTJOIN departments d ON e.`department_id` = d.`department_id` WHERE d.`department_id` ISNULL UNIONALL SELECT employee_id,department_name FROM employees e RIGHTJOIN departments d ON e.`department_id` = d.`department_id`;#123条记录
# 右下图:左中图 UNIONALL 右中图 SELECT employee_id,department_name FROM employees e LEFTJOIN departments d ON e.`department_id` = d.`department_id` WHERE d.`department_id` ISNULL UNIONALL SELECT employee_id,department_name FROM employees e RIGHTJOIN departments d ON e.`department_id` = d.`department_id` WHERE e.`department_id` ISNULL; #17条记录
SELECT employee_id,last_name,department_name FROM employees e JOIN departments d ON e.`department_id` = d.`department_id` AND e.`manager_id` = d.`manager_id`;
在 SQL99 中你可以写成:
1 2
SELECT employee_id,last_name,department_name FROM employees e NATURALJOIN departments d;
6.2 USING连接
当我们进行连接的时候,SQL99还支持使用 USING 指定数据表里的 同名字段 进行等值连接。但是只能配 合JOIN一起使用。比如:
1 2 3
SELECT employee_id,last_name,department_name FROM employees e JOIN departments d USING (department_id);
#关联条件 #把关联条件写在where后面 SELECT last_name,department_name FROM employees,departments WHERE employees.department_id = departments.department_id; #把关联条件写在on后面,只能和JOIN一起使用 SELECT last_name,department_name FROM employees INNERJOIN departments ON employees.department_id = departments.department_id; SELECT last_name,department_name FROM employees CROSSJOIN departments ON employees.department_id = departments.department_id; SELECT last_name,department_name FROM employees JOIN departments ON employees.department_id = departments.department_id; #把关联字段写在using()中,只能和JOIN一起使用 #而且两个表中的关联字段必须名称相同,而且只能表示= #查询员工姓名与基本工资 SELECT last_name,job_title FROM employees INNERJOIN jobs USING(job_id); #n张表关联,需要n-1个关联条件 #查询员工姓名,基本工资,部门名称 SELECT last_name,job_title,department_name FROM employees,departments,jobs WHERE employees.department_id = departments.department_id AND employees.job_id = jobs.job_id; SELECT last_name,job_title,department_name FROM employees INNERJOIN departments INNERJOIN jobs ON employees.department_id = departments.department_id AND employees.job_id = jobs.job_id;
注意: 我们要 控制连接表的数量 。多表连接就相当于嵌套 for 循环一样,非常消耗资源,会让 SQL 查询性能下降得很严重,因此不要连接不必要的表。在许多 DBMS 中,也都会有最大连接表的限制。
#11. SQL99语法的新特性2:USING SELECT employee_id,last_name,department_name FROM employees e JOIN departments d ON e.department_id = d.department_id;
SELECT employee_id,last_name,department_name FROM employees e JOIN departments d USING (department_id);
#拓展写法: SELECT last_name,job_title,department_name FROM employees INNERJOIN departments INNERJOIN jobs ON employees.department_id = departments.department_id AND employees.job_id = jobs.job_id; 123456789101112131415161718192021222324252627282930313233343536
SELECT* FROM employees WHERE commission_pct ISNOT NULL; #35条记录
#练习 SELECT employee_id FROM departments d LEFTJOIN employees e ON d.`department_id`=e.`department_id` WHERE d.`department_name` ='Sales'OR d.`department_name` ='Sales';
# 4.选择city在Toronto工作的员工的 last_name , job_id , department_id , department_name SELECT e.last_name , e.job_id , e.department_id , d.department_name FROM employees e JOIN departments d ON e.`department_id` = d.`department_id` JOIN locations l ON d.`location_id` = l.`location_id` WHERE l.`city` ='Toronto'; /* +-----------+--------+---------------+-----------------+ | last_name | job_id | department_id | department_name | +-----------+--------+---------------+-----------------+ | Hartstein | MK_MAN | 20 | Marketing | | Fay | MK_REP | 20 | Marketing | +-----------+--------+---------------+-----------------+ */
#sql92语法: SELECT e.last_name , e.job_id , e.department_id , d.department_name FROM employees e,departments d ,locations l WHERE e.`department_id` = d.`department_id` AND d.`location_id` = l.`location_id` AND l.`city` ='Toronto';