DB 数据库
DBMS 数据库管理系统
SQL 结构化查询语言
常见命令(cmd)
1. show databases; //打开当前所有数据库
2. use 库名 //打开指定库
3. show tables; //查看当前库的所有表
4. show tables from 库名 //查看其他库的所有表
5. create table 表名(
列表 列表类型,); //创建表
6. desc 表名 //查看表结构
7. select version(); //查看服务器版本 (mysql --version或 mysql -V 没有进入mysql时的命令)
8. 命令以分号或\g结尾
9. 注释
单行注释 1. #注释内容 或者 2. -- 注释内容(中间右空格)
多行注释 /* 注释内容 */
进阶一
基础查询
语法:
F12格式化代码
select 查询列表 from 表名;
类似于 System.out.println(打印东西);
特点:
1. 查询列表可以是:表中的字段、常量值、表达式、函数
2. 查询的结果是一个虚拟的表格
USE employees;
#1.查询表中的单个字段
SELECT last_name FROM employees;
#2.查询表中的多个字段
SELECT last_name,email,salary FROM employees;
#3.查询表中所有字段
#方式一
SELECT
`employee_id`,
`first_name`,
`last_name`,
`email`,
`phone_number`,
`job_id`,
`salary`,
`commission_pct`,
`manager_id`,
`department_id`,
`hiredate`
FROM
employees ;
#方式二
SELECT * FROM employees;
#4.查询常量值
SELECT 100;
SELECT 'join';
#5.查询表达式
SELECT 100*98;
#6.查询函数
SELECT VERSION();
#7.起别名
/*
1.便于理解
2.如果要查询的字段有重名的情况,使用别名可以区分开来
*/
SELECT 100*98 AS 结果;
#方式一
SELECT first_name AS 姓,last_name AS 名 FROM employees;
#方式二
SELECT first_name 姓,last_name 名 FROM employees;
#案例 查询salary 结果显示为 out put
SELECT salary AS "out put" FROM employees;
#8.去重
#案例 查询员工表中涉及到的所有部门编号
SELECT DISTINCT department_id FROM employees;
#9.+号的作用
/*
java中的+号
1.运算符 两个操作数都为数值型
2.连接符 至少有一个操作数为字符串
mysql中的+
仅仅只有一个功能:运算符
select 100+90; 两个操作数都为数值型
select '123'+90; 其中一方为字符型 试图将字符型转换为数值型
如果转换成功 则继续做加法运算
select 'join'+90; 如果转换失败 则将字符型数值转换为0
select null+10; 只要其中一方为null 则结果为null
*/
#案例:查询员工名和姓 并连接成一个字段 显示为 姓名
SELECT CONCAT(last_name,first_name) AS 姓名 FROM employees;
#10.显示结构
DESC departments;
#判断是否为空 如果为空 则用0代替
SELECT IFNULL(commission_pct,0) AS 奖金率 FROM employees;
进阶二
条件查询
#进阶二 条件查询
/*
语法:
select 查询列表 3
from 表名 1
where 筛选条件 2 (1 2 3代表执行顺序
分类
一、按条件表达式筛选
条件运算符:> < = != <>(等价于!=) >= <=
二、按逻辑表达式筛选
逻辑运算符: && || !
and or not
三、模糊查询
like
between and
in
is null
*/
#一、按条件表达式筛选
#案例一、查询工资>12000的员工信息
SELECT
*
FROM
employees
WHERE salary > 12000 ;
#案例二、查询部门编号不等于90的员工名和部门编号
SELECT
last_name,
department_id
FROM
employees
WHERE department_id <> 90 ;
#二、按逻辑表达式筛选
#案例1、工资在10000到20000之间的员工名、工资以及奖金
SELECT
last_name,
salary,
commission_pct
FROM
employees
WHERE salary >= 10000
AND salary <= 20000 ;
#案例2、查询部门编号不是在90到110之间的,或工资高于15000的员工信息
SELECT
*
FROM
employees
WHERE NOT (
department_id >= 90
AND department_id <= 110
)
OR salary > 15000 ;
#三、模糊查询
/*
like
特点:
一般和通配符搭配使用
通配符
% 任意多个字符 包含0个字符
_ 任意单个字符
between and
in
is null
*/
#1.like
#案例1.查询员工名中包含字符a的员工信息
SELECT
*
FROM
employees
WHERE last_name LIKE '%a%' ;
#案例2.查询员工名中第三个字符为n第五个字段为l的员工名和工资
SELECT
last_name,
salary
FROM
employees
WHERE last_name LIKE '__n_l%' ;
#案例3.查询员工名中第三个字符为_的员工名
SELECT
last_name
FROM
employees
WHERE last_name LIKE '_$_%' ESCAPE '$';#转义字符 escape
#2.between and
/*
提高语句简洁度
包含临界值
*/
#案例1.查询员工编号在100到120之间的员工信息
SELECT * FROM employees WHERE employee_id BETWEEN 100 AND 120;
#3.in
/*
含义:判断某字段是否属于In列表中的某一项
特点:提高语句简洁度
in列表的值类型必须统一或兼容
*/
#案例:查询员工的工种编号使 IT_PROG、AD_VP、AD_PRES中的一个的员工名和工种编号
SELECT last_name,job_id FROM employees WHERE job_id IN('IT_PROG','AD_VP','AD_PRES');
#4.is null
/*
=或<>不能用于判断null值
is null 或is not null 可以判断
*/
#案例:查询没有奖金的员工名和奖金率
SELECT last_name,commission_pct FROM employees WHERE commission_pct IS NOT NULL;
#安全等于 <=>
SELECT last_name,commission_pct FROM employees WHERE commission_pct <=> NULL;
#查询工资为12000的员工信息
SELECT * FROM employees WHERE salary <=> 12000;
/*
is null 仅仅可以判断NULL值 可读性高
<=> 不仅可以判断NULL值 还可以判断普通值 可读性低
*/
#查询员工号为176的员工姓名和部门号和年薪
SELECT last_name,department_id,salary*12*(1+IFNULL(commission_pct,0)) AS 年薪 FROM employees WHERE employee_id =176;
进阶三:排序查询
#进阶三:排序查询
/*
语法:
select 查询列表
from 表
[where 筛选条件]
order by 筛选列表 [esc|desc]
特点:
1. asc代表升序 desc代表降序 3执行顺序
如果不写 默认是升序 1
2. order by 子句中可以是单个字段、多个字段、表达式、函数、别名 2
3. order by 一般放在语句的最后面 limit子句除外 4
*/
#案例1:查询员工信息 工资从高到低排序
SELECT * FROM employees ORDER BY salary DESC;
#案例2:查询部门编号>=90的员工信息 按入职时间先后排序【添加筛选条件】
SELECT * FROM employees WHERE department_id>=90 ORDER BY hiredate ASC;
#案例3:按年薪的高低显示员工信息【按表达式排序】
SELECT *,salary*12*(1+IFNULL(commission_pct,0)) 年薪
FROM employees
ORDER BY salary*12*(1+IFNULL(commission_pct,0)) DESC;
#案例4:按年薪的高低显示员工信息【按别名排序】
SELECT *,salary*12*(1+IFNULL(commission_pct,0)) 年薪 FROM employees ORDER BY 年薪 DESC;
#案例5:按姓名长度显示员工姓名和工资【按函数排序】
SELECT LENGTH(last_name) 长度,last_name,salary FROM employees ORDER BY LENGTH(last_name) DESC;
#案例6:查询员工信息,要求先按工资排序升序,再按员工编号排序降序【按多个字段排序】
SELECT * FROM employees ORDER BY salary ASC,employee_id DESC;
SELECT last_name,salary*12*(1+IFNULL(commission_pct,0)) 年薪 FROM employees ORDER BY 年薪 DESC,last_name ASC;
SELECT last_name,salary FROM employees WHERE salary NOT BETWEEN 8000 AND 17000 ORDER BY salary DESC;
SELECT *,LENGTH(email) FROM employees WHERE email LIKE '%e%' ORDER BY LENGTH(email) DESC,department_id ASC;
进阶四 常见函数
#进阶四 常见函数
/*
概念:类似于Java中的方法,将一组逻辑语句封装在方法体中,对外暴露方法名
好处:1.隐藏了实现细节
调用:select 函数名(实参列表) 【from 表】;
特点:
1.函数名
2.函数功能
分类:
1.单行函数
如concat、ifnull、length
2.分组函数
功能:做统计使用,又称统计函数、聚合函数、组函数
*/
#一、字符函数
#1. length 获得参数值的字节个数
SELECT LENGTH('john');
SELECT LENGTH('张三');
SHOW VARIABLES LIKE '%char%';
#2. concat 拼接字符串
SELECT CONCAT(first_name,'_',last_name) AS 姓名 FROM employees;
#3. upper, lower
SELECT UPPER('john');
SELECT LOWER('joHn');
#案例:将姓变大写,名变小写,然后拼接
SELECT CONCAT(UPPER(last_name),LOWER(first_name)) FROM employees;
#4. substr、substring
#注意:索引从1开始
#截取从指定索引处后面所有字符
SELECT SUBSTR('werhrjohi',3);
#截取从指定索引开始指定长度的字符
SELECT SUBSTR('atearhe',4,2);
#案例:姓名中首字符大写,其他的小写,然后用_拼接
SELECT CONCAT(UPPER(SUBSTR(last_name,1,1)),'_',LOWER(SUBSTR(last_name,2))) FROM employees;
#5. instr
#返回字串第一次在模式串中出现的位置
SELECT INSTR('opiuykjoi','iuy') AS out_put;
#6. trim
#去掉首尾空格
SELECT LENGTH(TRIM(' resf ')) AS out_put;
#去掉首尾的a
SELECT TRIM('a' FROM 'aaaa张三aaaa帅aaaa') AS out_put;
#7. lpad
#用指定字符左填充指定长度
SELECT LPAD('adfg',10,'*') AS out_put;
#8. rpad
#用指定字符右填充指定长度
SELECT RPAD('adfg',10,'*') AS out_put;
#9. replace
#将所有的ert替换为ghjkl
SELECT REPLACE('qwertyuiop','ert','ghjkl') AS out_put;
#二、数学函数
#1. round 四舍五入
SELECT ROUND(1.567);
#小数点后保留3位
SELECT ROUND(1.56789876,3);
#2. ceil 向上取整
SELECT CEIL(1.3);
#3. floor 向下取整
SELECT FLOOR(1.9);
#4. truncate 截断
#从小数点后两位开始截断
SELECT TRUNCATE(1.8978,2);
#5. mod 取余
/*
mod(a,b) 如果a>0则结果为整数,如果a<0则结果为负数
*/
SELECT MOD(10,3);
#三、日期函数
#1. now返回系统当前日期+时间
SELECT NOW();
#2. curdate返回当前系统日期 不包含时间
SELECT CURDATE();
#3. curtime返回当前系统时间 不包含日期
SELECT CURTIME();
#获取指定部分
SELECT YEAR(NOW()) AS 年;
SELECT MONTH(NOW()) AS 月;
SELECT MONTHNAME(NOW()) AS 月;
SELECT DAY(NOW()) AS 日;
SELECT HOUR(NOW()) AS 时;
SELECT MINUTE(NOW()) AS 分;
SELECT SECOND(NOW()) AS 秒;
#4. str_to_date 将字符转换成日期
SELECT STR_TO_DATE('21-8-5','%Y-%c-%d');
#5. date_format 将日期转换成字符
SELECT DATE_FORMAT(NOW(),'%y年%m月%d日');
#查询有奖金的员工的工资和入职日期(%m月/%d日 %y 年)
SELECT salary,DATE_FORMAT(hiredate,'%m月/%d日 %y年') AS out_put FROM employees WHERE commission_pct IS NOT NULL
#四、其他函数
SELECT VERSION();
SELECT DATABASE();
SELECT USER();
#五、流程控制函数
#1. if函数
#相当于三目运算符
SELECT IF(10>5,'大','小');
SELECT last_name,commission_pct,IF(commission_pct IS NULL,'没奖金','有奖金') FROM employees;
#2. case函数
/*
java中
switch()
{
case 常量1:语句1;break;
default:常量n;break;
}
mysql 中
case 要判断的字段或表达式
when 常量1 then 要显示的值1或语句1;
when 常量1 then 要显示的值1或语句1;
else 常量n then 要显示的值n或语句n;
end
*/
/*#案例:查询员工工资
部门号=30 显示工资的1.1倍
部门号=40 显示工资的1.2倍
部门号=50 显示工资的1.3倍
其他部门 显示原工资
*/
SELECT salary 原始工资,department_id,
CASE department_id
WHEN 30 THEN salary*1.1
WHEN 40 THEN salary*1.2
WHEN 50 THEN salary*1.3
ELSE salary
END AS 新工资
FROM employees;
#3. case使用 类似于多种if
/*
java中
if(条件1){
语句1;
}
else if(条件2){
语句2;
}
else{
语句n;
}
mysql中
case
when 条件1 then 要显示的值1
when 条件2 then 要显示的值2
else 要显示的值n
end
*/
#案例:查询员工工资
/*
如果>20000 显示A级别
如果>15000 显示B级别
如果>10000 显示C级别
否则 显示D级别
*/
SELECT salary,
CASE
WHEN salary>=20000 THEN 'A'
WHEN salary>=15000 THEN 'B'
WHEN salary>=10000 THEN 'C'
ELSE 'D'
END AS 工资级别
FROM employees;
#分组函数
/*
功能:用作统计使用 又称聚合函数或统计函数 组函数
分类:max sum min avg count
sum avg 处理数值型
max min count 可以处理任意类型
2. 以上分组函数都忽略null值
3. 可以和distinct搭配使用
4.count函数的详细介绍
一般用count(*)统计数
5.和分组函数一起查询的字段要求是group by 后的字段
*/
SELECT SUM(salary) FROM employees;
SELECT MIN(salary) FROM employees;
SELECT MAX(salary) FROM employees;
SELECT AVG(salary) FROM employees;
SELECT COUNT(salary) FROM employees;
#2. 参数支持哪些类型
#3. 忽略null值
#4. 和distinct搭配
SELECT SUM(DISTINCT salary) ,SUM(salary) FROM employees;
SELECT COUNT(salary),COUNT(DISTINCT salary) FROM employees;
#5.count函数的详细介绍
SELECT COUNT(*) FROM employees;
SELECT ocunt(1) FROM employees;
#和分组函数一起查询的字段有限制
SELECT AVG(salary),employee_id FROM employees;
#查询员工表中的最大入职时间和最小入职时间的相隔天数
SELECT DATEDIFF(MAX(hiredate),MIN(hiredate)) FROM employees;
#查询员工编号为90 的员工个数
SELECT COUNT(*) FROM employees WHERE employee_id=90;
进阶五 分组查询
#进阶五 分组查询
/*
select 分组函数,列(要求出现在group by 的后面)
from 表
【where 筛选条件】
group by 分组的列表
【order by 子句】
注意:查询列表比较特殊 要求是分组函数和group by 后出现的字段
特点:
1.分组查询中的筛选条件分为两类
筛选源
分组前筛选 原始表
分组后筛选 分组后的结果集
分组函数做条件肯定是放在having子句中
能用分组前筛选的 优先考虑使用分组前筛选
2.group by 子句支持单个字段分组 多个字段分组(多个字段之间用逗号隔开) 表达式或函数
3.也可以添加排序(放在整个分组查询的最后
*/
#简单分组查询
#查询每个工种的最高工资
SELECT MAX(salary),job_id FROM employees GROUP BY job_id;
#查询每个位置上的部门个数
SELECT COUNT(*) FROM departments GROUP BY location_id;
#添加筛选条件
#案例1:查询邮箱中包含字符'a'的每个部门的最低工资
SELECT MIN(salary),department_id FROM employees WHERE email LIKE '%a%' GROUP BY department_id;
#案例2.查询有奖金的领导的手下员工的最高工资
SELECT MAX(salary),manager_id FROM employees WHERE commission_pct IS NOT NULL GROUP BY manager_id;
#添加复杂筛选条件(添加分组后)
#案例1:哪个部门的员工个数大于2
#1.查询每个部门员工个数
#首先查询每个部门的员工个数
SELECT COUNT(*) department_id FROM employees GROUP BY department_id;
#其次查询员工个数大于2 的部门
SELECT COUNT(*) department_id FROM employees GROUP BY department_id HAVING COUNT(*)>2;
#案例2:查询每个工种有奖金的员工的最高工资>12000的工种编号和最高工资
#首先 查询每个工种有奖金的员工的最高工资
SELECT MAX(salary),job_id FROM employees WHERE commission_pct IS NOT NULL GROUP BY job_id;
#其次 筛选最高工资大于12000
SELECT MAX(salary),job_id FROM employees WHERE commission_pct IS NOT NULL GROUP BY job_id HAVING MAX(salary)>12000;
#案例3:查询领导编号>102的每个领导手下最低工资>5000的领导编号是哪个 以及最低工资
#首先查询每个领导手下的员工最低工资
SELECT MIN(salary),manager_id FROM employees GROUP BY manager_id;
#其次编号>102
SELECT MIN(salary),manager_id FROM employees WHERE manager_id>102 GROUP BY manager_id;
#最后 最低工资大于5000
SELECT MIN(salary),manager_id FROM employees WHERE manager_id>102 GROUP BY manager_id HAVING MIN(salary)>5000;
#按表达式或函数分组
#案例:按员工个数分组 查询每一组的员工个数 筛选员工个数大于5的有哪些
#查询每个长度的员工个数
SELECT COUNT(*),LENGTH(last_name) FROM employees GROUP BY LENGTH(last_name);
#添加筛选条件
SELECT COUNT(*),LENGTH(last_name) FROM employees GROUP BY LENGTH(last_name) HAVING COUNT(*)>5;
#按多字段分组
#案例:查询每个部门每个工种的员工的平均工资
SELECT AVG(salary),department_id,job_id FROM employees GROUP BY department_id,job_id;
#添加排序
#案例:查询每个部门每个工种的员工的平均工资 并按高低显示
SELECT AVG(salary),department_id,job_id FROM employees GROUP BY department_id,job_id ORDER BY AVG(salary) DESC;
进阶六:连接查询
#进阶六、连接查询
/*
含义:又称多表查询 当查询的字段来自多个表时就会用到连接查询
笛卡尔乘积现象 表1有n行 表2有m行 结果为n*m行
发生原因:没有有效的连接条件
如何避免:添加有效的连接条件
分类:
按年代分类:
sq192标准:仅仅支持内连接
sq199标准
按功能分类:
内连接:
等值连接
非等值连接
自连接
外连接:
左外连接
右外连接
全外连接
交叉连接:
*/
SELECT * FROM beauty;
SELECT * FROM boys;
#一、sq192标准
/*
多表等值连接的结果为多表的交集部分
n表连接 至少有n-1个连接条件
多表的顺序没有要求
一般需要为表起别名
可以搭配前面将的所有语句使用 比如 排序 分组 筛选
*/
#1.等值连接
#案例1.:查询女神名和对应的男神名
SELECT `name`,boyName FROM boys,beauty WHERE beauty.`id`=boys.`id`;
#案例2.:查询员工名和对应的部门名
SELECT last_name,department_name FROM employees,departments WHERE employees.`department_id`=departments.`department_id`;
#2.为表起别名
/*
提高语句的简洁度
区分多个重名的字段
如果起了别名 则查询的时候不能使用原名去限定
*/
#查询员工名、工种号、工种名
SELECT e.last_name,e.job_id,department_id FROM employees e,jobs j WHERE e.`job_id`=j.`job_id`;
#3顺序可以变换
SELECT e.last_name,e.job_id,department_id FROM jobs j,employees e WHERE e.`job_id`=j.`job_id`;
#4是否可以加筛选
#案例1:查询有奖金的员工名 部门名
SELECT last_name,department_name,commission_pct FROM employees e,departments d WHERE e.`department_id`=d.`department_id` AND e.`commission_pct` IS NOT NULL;
#案例2:查询城市名中第二个字母为o的城市名和部门名
SELECT department_name,city FROM departments d,locations l WHERE d.`location_id`=l.`location_id` AND l.`city` LIKE '_o%';
#5可以加分组
#案例1:查询每个城市的部门个数
SELECT COUNT(*),city FROM departments d,locations l WHERE d.`location_id`=l.`location_id` GROUP BY city;
#案例2:查询有奖金的部门的部门名和领导编号和该部门的最低工资
SELECT department_name,d.`manager_id`,MIN(salary)
FROM departments d,employees e
WHERE d.`department_id`=e.`department_id`
AND commission_pct IS NOT NULL
GROUP BY department_name,d.`manager_id`;
#6可以加排序
#案例1:查询每个工种的工种名和员工个数 并按员工个数降序
SELECT job_title,COUNT(*) FROM jobs j,employees e WHERE j.`job_id`=e.`job_id` GROUP BY j.`job_title` ORDER BY COUNT(*) DESC;
#7可以实现三表连接
#案例:查询员工名、部门名和所在的城市
SELECT last_name,department_name,city FROM employees e,departments d,locations l WHERE l.`location_id`=d.`location_id` AND e.`department_id`=d.`department_id`;
#2、非等值连接
#案例:查询员工的工资和工资级别
SELECT salary,grade_level FROM employees e,job_grades g WHERE salary BETWEEN g.`lowest_sal` AND g.`highest_sal`;
#3、自连接
#案例:查询员工名以及上级名称
SELECT e.`last_name`,e.`employee_id`,m.`last_name`,m.`employee_id`
FROM employees e,employees m WHERE e.`manager_id`=m.`employee_id`;
#二、sql99用法
/*
语法:
select 查询列表
from 表1 别名【连接类型】
join 表2 别名
on 连接条件
where 筛选条件
group by 分组
having 筛选条件
order by 排序列表
内连接:inner
外连接
左外连接:left【outer】
右外连接:right【outer】
全外连接:full【outer】
交叉连接:cross
*/
*/
#一)内连接
/*
语法:
select 查询列表
from 表1 别名
inner join 表2 别名
on 连接条件
分类:
等值
非等值
自连接
特点:
添加排序 分组 筛选
inner可以省略
筛选条件放在where后面 连接条件放在on后面 提高了分离性 便于阅读
inner join连接和sql92语法中的等值连接实现的效果是一样的 都是查询交集部分
*/
#1等值连接
#案例1:查询员工名 部门名
SELECT last_name,department_name FROM employees e INNER JOIN departments d ON e.`department_id`=d.`department_id`;
#案例2:查询名字中包含e的员工名和工种名(添加筛选)
SELECT last_name,job_title FROM employees e INNER JOIN jobs j ON e.`job_id`=j.`job_id` WHERE e.`last_name` LIKE '%e%';
#案例3:查询部门个数>3的城市名和部门个数(添加分组+筛选)
SELECT city,COUNT(*) 部门个数 FROM departments d INNER JOIN locations l ON d.`location_id`=l.`location_id` GROUP BY city HAVING COUNT(*)>3;
#案例4:查询哪个部门的员工个数>3的部门名个员工个数 并按个数降序(添加排序)
#1 查询每个部门的员工个数
SELECT COUNT(*),department_name FROM employees e INNER JOIN departments d ON d.`department_id`=e.`department_id` GROUP BY department_name;
#2在1的基础上筛选员工个数>3的记录 并排序
SELECT COUNT(*),department_name FROM employees e INNER JOIN departments d ON d.`department_id`=e.`department_id` GROUP BY department_name
HAVING COUNT(*)>3 ORDER BY COUNT(*) DESC;
#案例5:查询员工名 部门名 工种名 并按部门名排序
SELECT last_name,department_name,job_title
FROM employees e
INNER JOIN departments d ON e.`department_id`=d.`department_id`
INNER JOIN jobs j ON j.`job_id`=e.`job_id`
ORDER BY department_name DESC;
#二)、非等值连接
#查询员工的工资级别
SELECT salary,grade_level
FROM employees e
INNER JOIN job_grades g
ON e.`salary` BETWEEN g.`lowest_sal` AND g.`highest_sal`;
#查询工资级别的个数>20的个数 并排序 降序
SELECT COUNT(*),grade_level
FROM employees e
INNER JOIN job_grades g
ON e.`salary` BETWEEN g.`lowest_sal` AND g.`highest_sal`
GROUP BY grade_level
HAVING COUNT(*)>20
ORDER BY grade_level DESC;
#三)、自连接
#查询名字中包含k的员工名字 上级名字
SELECT e.last_name,m.last_name
FROM employees e
INNER JOIN employees m
ON e.`manager_id`=m.`employee_id`
WHERE e.`last_name` LIKE '%k%';
#二、外连接
/*
应用场景:一个表中右,另一个表中没有的记录
特点:
1、外连接查询结果为主表中的所有记录
如果从表中有和它匹配的 则显示匹配的值
如果从表中没有和它匹配的 则显示null
外连接查询结果=内连接查询结果+主表中有二从表中没有的记录
2、左外连接中 left join 左边的是主表
右外连接中 right join 右边的是主表
3、左外和右外交换两个表的顺序 可以实现同样的效果
4、全外连接=内连接的结果+表1中有但表2中没有+表2中有但表1中没有
*/
USE girls;
#查询男朋友不在男神表中的女神名
#左外连接
SELECT b.name,bo.*
FROM beauty b
LEFT OUTER JOIN boys bo
ON b.`boyfriend_id`=bo.`id`
WHERE bo.`id` IS NULL;
#案例1:查询哪个部门没有员工
#左外
SELECT d.*,e.employee_id
FROM departments d
LEFT OUTER JOIN employees e
ON d.`department_id`=e.`department_id`
WHERE e.`department_id` IS NULL;
#全外
SELECT b.*,bo.*
FROM beauty b
FULL OUTER JOIN boys bo
ON b.`boyfriend_id`=bo.id;