DQL
基础查询
SELECT 要查询的东西 (FROM 表名);
# 通过select查询完的结果 ,是一个虚拟的表格,不是真实存在
# 要查询的东西 可以是常量值、可以是表达式、可以是字段、可以是函数
USE myemployees;
# 1.查询表中的单个字段
SELECT last_name FROM employees;
# 2.查询表中的多个字段
SELECT last_name,salary,email FROM employees;
# 3.查询表中的所有字段
#方式一:
SELECT
`employee_id`,
`first_name`,
`last_name`,
`phone_number`,
`last_name`,
`job_id`,
`phone_number`,
`job_id`,
`salary`,
`commission_pct`,
`manager_id`,
`department_id`,
`hiredate`
FROM
employees;
# `xxx`: `是着重号,用于区分字段和关键字
#方式二:
SELECT * FROM employees;
# 4.查询常量值
SELECT 100;
SELECT 'john';
# 5.查询表达式
SELECT 100%98;
# 6.查询函数
SELECT VERSION();
# 7.起别名
# ①便于理解
# ②如果要查询的字段有重名的情况,使用别名可以区分开来
#方式一:使用as
SELECT 100%98 AS 结果;
SELECT last_name AS 姓,first_name AS 名 FROM employees;
#方式二:使用空格
SELECT last_name 姓,first_name 名 FROM employees;
# 别名有特殊符号或关键字,则需把别名加上双引号
# 案例:查询salary,显示结果为 out put
SELECT salary AS "out put" FROM employees;
# 8.去重
#案例:查询员工表中涉及到的所有的部门编号
SELECT DISTINCT department_id FROM employees;
# 9. + 号的作用
/*
java中的+号:
①运算符,两个操作数都为数值型
②连接符,只要有一个操作数为字符串
mysql中的+号:
仅仅只有一个功能:运算符
select 100+90;(190) 两个操作数都为数值型,则做加法运算
select '123'+90;(113) 只要其中一方为字符型,试图将字符型数值转换成数值型,如果转换成功,则继续做数值的加法运算
select 'john+90;(90) 如果转换失败,则将字符型数值转换成0
select null+10;(null) 只要其中一方为null,则结果肯定为null
*/
#案例:查询员工名和姓连接成一个字段,并显示为 姓名
SELECT CONCAT('a','b','c') AS 结果;
SELECT
CONCAT(last_name,first_name) AS 姓名
FROM
employees;
条件查询
# 语法:
select
要查询的字段|表达式|常量值|函数
from
表
where
条件;
# 分类:
/*
一、条件表达式
示例:salary>10000
条件运算符:
> < >= <= = != <>
*/
#案例:查询部门编号不等于90号的员工名和部门编号
SELECT
last_name,
department_id
FROM
employees
WHERE
department_id<>90;
/*
二、逻辑表达式
示例:salary>10000 && salary<20000
逻辑运算符:
and(&&):两个条件如果同时成立,结果为true,否则为false
or(||):两个条件只要有一个成立,结果为true,否则为false
not(!):如果条件成立,则not后为false,否则为true
*/
#案例:查询部门编号不是在90到110之间,或者工资高于15000的员工信息
SELECT
*
FROM
employees
WHERE
NOT(department_id>=90 AND department_id<=110) OR salary>15000;
/*
三、模糊查询
示例:last_name like 'a%'
通配符:
% : 任意多个字符,包含0个字符;
_ : 任意单个字符;
运算符:
like: 一般和通配符搭配使用。
between XXX and XXX: 包含临界值;两个临界值不要调换顺序.
in: in列表的值类型必须一致或兼容;in列表中不支持通配符.
is null: =或<>不能用于判断null值
is not null
*/
#案例:查询员工名中第二个字符为_的员工名
SELECT
last_name
FROM
employees
WHERE
last_name LIKE '_$_%' ESCAPE '$'; # 指定$为转义字符,默认转义字符为"\"
#案例:查询员工编号在100到120之间的员工信息
SELECT
*
FROM
employees
WHERE
employee_id BETWEEN 120 AND 100;
#案例:查询员工的工种编号是 IT_PROG、AD_VP、AD_PRES中的一个员工名和工种编号
SELECT
last_name,
job_id
FROM
employees
WHERE
job_id IN( 'IT_PROT' ,'AD_VP','AD_PRES');
#案例:查询没有奖金的员工名和奖金率
SELECT
last_name,
commission_pct
FROM
employees
WHERE
commission_pct IS NULL;
/*
四、安全等于 <=>
*/
#案例:查询没有奖金的员工名和奖金率
SELECT
last_name,
commission_pct
FROM
employees
WHERE
commission_pct <=>NULL;
#案例:查询工资为12000的员工信息
SELECT
last_name,
salary
FROM
employees
WHERE
salary <=> 12000;
# IS NULL 与 <=>
# IS NULL:仅仅可以判断NULL值,可读性较高,建议使用
# <=> :既可以判断NULL值,又可以判断普通的数值,可读性较低
SELECT ISNULL(commission_pct) FROM employees;
面试题
问:select * from employees; 和 select * from employees where com mission_pct like ‘%%’ and last_name like ‘%%’;结果是否一样?为什么?
如果判断字段没有null,结果一致;若字段中含有null,则结果不一样!
排序查询
# 语法:
select
要查询的东西 # 执行次序:3
from
表 # 执行次序:1
where
条件 # 执行次序:2
order by 排序的字段|表达式|函数|别名 【asc|desc】 # 执行次序:4
/*
1、asc代表的是升序,可以省略,desc代表的是降序
2、order by子句可以支持 单个字段、别名、表达式、函数、多个字段
3、order by子句在查询语句的最后面,除了limit子句
*/
# 按单个字段排序
SELECT * FROM employees ORDER BY salary DESC;
# 添加筛选条件再排序
# 案例:查询部门编号>=90的员工信息,并按员工编号降序
SELECT *
FROM employees
WHERE department_id>=90
ORDER BY employee_id DESC;
# 按表达式排序
# 案例:查询员工信息 按年薪降序
SELECT *,salary*12*(1+IFNULL(commission_pct,0))
FROM employees
ORDER BY salary*12*(1+IFNULL(commission_pct,0)) DESC;
# 按函数排序
# 案例:查询员工名,并且按名字的长度降序
SELECT LENGTH(last_name),last_name
FROM employees
ORDER BY LENGTH(last_name) DESC;
# 按多个字段排序
# 案例:查询员工信息,要求先按工资降序,再按employee_id升序
SELECT *
FROM employees
ORDER BY salary DESC,employee_id ASC;
常见函数
字符函数
- concat 拼接
- substr 截取子串
- upper 转换成大写
- lower 转换成小写
- trim 去前后指定的空格和字符
- ltrim 去左边空格
- rtrim 去右边空格
- replace 替换
- lpad 左填充
- rpad 右填充
- instr 返回子串第一次出现的索引,如果找不到就返回0
- length 获取字节个数
```mysql
SHOW VARIABLES LIKE ‘%char%’; # 可以查看编码utf-8####### 注意:索引从1开始
截取从 指定索引处 后面 所有字符
SELECT SUBSTR(‘李莫愁爱上了陆展元’,7) out_put; # 陆展元
截取从 指定索引处 指定字符 长度 的字符
SELECT SUBSTR(‘李莫愁爱上了陆展元’,1,3) out_put; # 李莫愁
SELECT LENGTH(TRIM(‘ 张翠山 ‘)) AS out_put; # 9
SELECT TRIM(‘aa’ FROM ‘aaaaaaaaa张aaaaaaaaaaaa翠山aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa’) AS out_put; # 只能去除两边的,中间不去
lpad 用指定的字符实现左填充指定长度,第二个参数:填充后的长度
SELECT LPAD(‘殷素素’,2,’*’) AS out_put; # 殷素
rpad 用指定的字符实现右填充指定长度
SELECT RPAD(‘殷素素’,12,’ab’) AS out_put;
#### 数学函数
- round 四舍五入
- rand 随机数
- floor 向下取整
- ceil 向上取整
- mod 取余
- runcate 截断
- ```mysql
#round 四舍五入
SELECT ROUND(-1.55);
SELECT ROUND(1.567,2); # 第二个参数:有效数字
#truncate 截断
SELECT TRUNCATE(1.69999,1); # 小数位数,直接截断
日期函数
- now 当前系统日期+时间
- curdate 当前系统日期
- curtime 当前系统时间
- str_to_date 将字符转换成日期
- date_format 将日期转换成字符
- year 返回当前年
- month 返回当前月
- day 返回当前天
- hour 返回当前小时
- minute 返回当前分钟
- second 返回当前秒
- datediff 返回两个日期的差
- monthname 以英文形式返回月份
#可以获取指定的部分,年、月、日、小时、分钟、秒 SELECT YEAR(NOW()) 年; SELECT YEAR('1998-1-1') 年; SELECT YEAR(hiredate) 年 FROM employees; SELECT MONTH(NOW()) 月; SELECT MONTHNAME(NOW()) 月; # str_to_date 将字符通过指定的格式转换成日期 SELECT STR_TO_DATE('1998-3-2','%Y-%c-%d') AS out_put; #查询入职日期为1992-4-3的员工信息 SELECT * FROM employees WHERE hiredate = '1992-4-3'; SELECT * FROM employees WHERE hiredate = STR_TO_DATE('4-3 1992','%c-%d %Y'); # date_format 将日期转换成字符 SELECT DATE_FORMAT(NOW(),'%y年%m月%d日') AS out_put; #查询有奖金的员工名和入职日期(xx月/xx日 xx年) SELECT last_name,DATE_FORMAT(hiredate,'%m月/%d日 %y年') 入职日期 FROM employees WHERE commission_pct IS NOT NULL; # 查询员工表中的最大入职时间和最小入职时间的相差天数 (DIFFRENCE) SELECT MAX(hiredate) 最大,MIN(hiredate) 最小,(MAX(hiredate)-MIN(hiredate))/1000/3600/24 DIFFRENCE FROM employees; SELECT DATEDIFF(MAX(hiredate),MIN(hiredate)) DIFFRENCE FROM employees; SELECT DATEDIFF('1995-2-7','1995-2-6');
流程控制函数
if(条件表达时, 表达式1, 表达式2) 处理双分支
case语句 处理多分支
- 情况1:处理等值判断
- 情况2:处理条件判断
SELECT IF(10<5,'大','小');
SELECT last_name,commission_pct,IF(commission_pct IS NULL,'没奖金,呵呵','有奖金,嘻嘻') 备注
FROM employees;
/*
java中
switch(变量或表达式){
case 常量1:语句1;break;
...
default:语句n;break;
}
mysql中:
case 要判断的字段或表达式
when 常量1 then 要显示的值1或语句1;
when 常量2 then 要显示的值2或语句2;
else 要显示的值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;
#案例:查询员工的工资的情况
如果工资>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;
其他函数
- version 版本
- database 当前库
- user 当前连接用户
- password(‘字符’) 返回该字符的加密形式
- md5(‘字符’) 返回该字符的md5加密形式
分组函数
- sum 求和
- max 最大值
- min 最小值
- avg 平均值
- count 计数
特点:
1、以上五个分组函数都忽略null值,除了count(*)
2、sum和avg一般用于处理数值型;max、min、count可以处理任何数据类型
3、都可以搭配distinct使用,用于统计去重后的结果
4、count的参数可以支持:字段、*、常量值; count(*) 和count(1)用来结果集的行数;count(字段)。
效率:
MYISAM存储引擎下 ,COUNT(*)的效率高
INNODB存储引擎下,COUNT(*)和COUNT(1)的效率差不多,比COUNT(字段)要高一些5、和分组函数一同查询的字段要求是group by后的字段
6、和分组函数一同查询的字段有限制:
SELECT AVG(salary),employee_id FROM employees;# “employee_id”无意义
谓词函数
- LIKE
- BETWEEN
- IS NULL、IS NOT NULL
- IN
- EXISTS (链接)
# LIKE谓词 – 用于字符串的部分一致查询。部分一致大体可以分为前方一致、中间一致和后方一致三种类型。
SELECT *
FROM samplelike
WHERE strcol LIKE 'ddd%'; # # %是代表“零个或多个任意字符串”的特殊符号,
SELECT *
FROM samplelike
WHERE strcol LIKE 'abc__'; # _下划线匹配任意 1 个字符
# BETWEEN谓词 – 用于范围查询
SELECT product_name, sale_price
FROM product
WHERE sale_price BETWEEN 100 AND 1000; # BETWEEN 的特点就是结果中会包含 100 和 1000 这两个临界值,也就是闭区间。如果不想让结果中包含临界值,那就必须使用 < 和 >。
SELECT product_name, sale_price
FROM product
WHERE sale_price > 100
AND sale_price < 1000;
# IS NULL、 IS NOT NULL – 用于判断是否为NULL
# 为了选取出某些值为 NULL 的列的数据,不能使用 =,而只能使用特定的谓词IS NULL。
SELECT product_name, purchase_price
FROM product
WHERE purchase_price IS NULL;
SELECT product_name, purchase_price
FROM product
WHERE purchase_price IS NOT NULL;
# IN谓词 – OR的简便用法
# 多个查询条件取并集时可以选择使用or语句。
SELECT product_name, purchase_price
FROM product
WHERE purchase_price = 320
OR purchase_price = 500
OR purchase_price = 5000;
SELECT product_name, purchase_price
FROM product
WHERE purchase_price IN (320, 500, 5000); / WHERE purchase_price NOT IN (320, 500, 5000); # 需要注意的是,在使用IN 和 NOT IN 时是无法选取出NULL数据的。实际结果也是如此,上述两组结果中都不包含进货单价为 NULL 的叉子和圆珠笔。 NULL 只能使用 IS NULL 和 IS NOT NULL 来进行判断。
# EXIST谓词的使用方法。谓词的作用就是 “判断是否存在满足某种条件的记录”,判断子查询得到的结果集是否是一个空集,如果不是,则返回 True,如果是,则返回 False。
SELECT product_name, sale_price
FROM product AS p
WHERE EXISTS (SELECT *
FROM shopproduct AS sp
WHERE sp.shop_id = '000C'
AND sp.product_id = p.product_id);
分组查询
/*
语法:
select 查询列表
from 表
【where 筛选条件】
group by 分组的字段
【having 分组后的筛选】
【order by 排序的字段】;
特点:
1、和分组函数一同查询的字段必须是group by后出现的字段
2、筛选分为两类:分组前筛选和分组后筛选
针对的表 位置 连接的关键字
分组前筛选 原始表 group by前 where
分组后筛选 group by后的结果集 group by后 having
问题1:分组函数做筛选能不能放在where后面
答:不能
问题2:where——group by——having
一般来讲,能用分组前筛选的,尽量使用分组前筛选,提高效率
3、分组可以按单个字段也可以按多个字段(多个字段用逗号隔开没有顺序要求)
4、可以搭配着排序使用,放在最后
*/
#查询每个工种的员工平均工资
SELECT AVG(salary),job_id
FROM employees
GROUP BY job_id;
#查询有奖金的每个领导手下员工的平均工资
SELECT AVG(salary),manager_id
FROM employees
WHERE commission_pct IS NOT NULL
GROUP BY manager_id;
#分组后筛选
#案例:查询哪个部门的员工个数>5
SELECT COUNT(*),department_id
FROM employees
GROUP BY department_id
HAVING COUNT(*)>5;
#领导编号>102的每个领导手下的最低工资大于5000的领导编号和最低工资
SELECT manager_id,MIN(salary)
FROM employees
WHERE manager_id>102
GROUP BY manager_id
HAVING MIN(salary)>5000;
#5.按多个字段分组
#查询每个工种每个部门的最低工资,并按最低工资降序
SELECT MIN(salary),job_id,department_id
FROM employees
GROUP BY department_id,job_id
ORDER BY MIN(salary) DESC;
连接查询
/*
含义:又称多表查询,当查询的字段来自于多个表时,就会用到连接查询
笛卡尔乘积现象:表1 有m行,表2有n行,结果=m*n行
发生原因:没有有效的连接条件
如何避免:添加有效的连接条件
分类:
按年代分类:
sql92标准:仅仅支持内连接
sql99标准【推荐】:支持内连接+外连接(左外和右外)+交叉连接
按功能分类:
内连接:
等值连接
非等值连接
自连接
外连接:
左外连接
右外连接
全外连接(mysql不支持)
交叉连接
*/
sql92标准
#一、sql92标准
#1、等值连接
/*
① 多表等值连接的结果为多表的交集部分
② n表连接,至少需要n-1个连接条件
③ 多表的顺序没有要求
④ 一般需要为表起别名
⑤ 可以搭配前面介绍的所有子句使用,比如排序、分组、筛选
语法格式:
select 查询列表
from 表1,表2
where 等值条件
【and 筛选条件】
【group by 分组字段】
【having 分组后的筛选】
【order by 排序字段】
*/
#查询员工名和对应的部门名
SELECT last_name,department_name
FROM employees,departments
WHERE employees.`department_id`=departments.`department_id`;
/* ①提高语句的简洁度
②区分多个重名的字段
注意:如果为表起了别名,则查询的字段就不能使用原来的表名去限定*/
#查询员工名、工种号、工种名
SELECT e.last_name,e.job_id,j.job_title
FROM employees e,jobs j
WHERE e.`job_id`=j.`job_id`;
#案例:查询有奖金的员工名、部门名
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;
#查询有奖金的每个部门的部门名和部门的领导编号和该部门的最低工资
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`;
#三表连接:查询员工名、部门名和所在的城市
SELECT last_name,department_name,city
FROM employees e,departments d,locations l
WHERE e.`department_id`=d.`department_id`
AND d.`location_id`=l.`location_id`
AND city LIKE 's%'
ORDER BY department_name DESC;
#2、非等值连接
#查询员工的工资和工资级别
SELECT salary,grade_level
FROM employees e,job_grades g
WHERE salary BETWEEN g.`lowest_sal` AND g.`highest_sal`
AND g.`grade_level`='A';
#3、自连接
#案例:查询 员工名和上级的名称
SELECT e.employee_id,e.last_name,m.employee_id,m.last_name
FROM employees e,employees m
WHERE e.`manager_id`=m.`employee_id`;
sql99语法
#二、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 连接条件;
分类:
等值
非等值
自连接
自然连结(NATURAL JOIN)
特点:
1 添加排序、分组、筛选
2 inner可以省略
3 筛选条件放在where后面,连接条件放在on后面,提高分离性,便于阅读
4 inner join连接和sql92语法中的等值连接效果是一样的,都是查询多表的交集
5 SELECT 子句中的列最好按照 表名.列名 的格式来使用.
*/
#等值连接
#查询员工名、部门名
SELECT last_name,department_name
FROM departments d
INNER JOIN employees e
ON e.`department_id` = d.`department_id`;
#查询员工名、部门名、工种名,并按部门名降序(添加三表连接)
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 e.`job_id` = j.`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;
#自连接
#查询员工的名字、上级的名字
SELECT e.last_name,m.last_name
FROM employees e
JOIN employees m
ON e.`manager_id`= m.`employee_id`;
#查询姓名中包含字符k的员工的名字、上级的名字
SELECT e.last_name,m.last_name
FROM employees e
JOIN employees m
ON e.`manager_id`= m.`employee_id`
WHERE e.`last_name` LIKE '%k%';
# 自然连结并不是区别于内连结和外连结的第三种连结, 它其实是内连结的一种特例–当两个表进行自然连结时, 会按照两个表中都包含的列名来进行等值内连结, 此时无需使用 ON 来指定连接条件.
# 把两个表的公共列(这里是 product_id, 可以有多个公共列)放在第一列, 然后按照两个表的顺序和表中列的顺序, 将两个表中的其他列都罗列出来。
SELECT * FROM shopproduct NATURAL JOIN Product
外连接
/*
应用场景:用于查询一个表中有,另一个表没有的记录
特点:
1、外连接的查询结果为主表中的所有记录
如果从表中有和它匹配的,则显示匹配的值
如果从表中没有和它匹配的,则显示null
外连接查询结果=内连接结果+主表中有而从表没有的记录
2、左外连接,left join左边的是主表
右外连接,right join右边的是主表
3、左外和右外交换两个表的顺序,可以实现同样的效果
4、全外连接=内连接的结果+表1中有但表2没有的+表2中有但表1没有的
*/
#引入:查询男朋友 不在男神表的的女神名
#左外连接
SELECT b.*,bo.*
FROM boys bo
LEFT OUTER JOIN beauty b
ON b.`boyfriend_id` = bo.`id`
WHERE bo.`id` IS NULL;
#查询哪个部门没有员工
#左外
SELECT d.*,e.employee_id
FROM departments d
LEFT OUTER JOIN employees e
ON d.`department_id` = e.`department_id`
WHERE e.`employee_id` IS NULL;
#右外
SELECT d.*,e.employee_id
FROM employees e
RIGHT OUTER JOIN departments d
ON d.`department_id` = e.`department_id`
WHERE e.`employee_id` IS NULL;
#全外
USE girls;
SELECT b.*,bo.*
FROM beauty b
FULL OUTER JOIN boys bo
ON b.`boyfriend_id` = bo.id;
#交叉连接,实现效果和笛卡尔乘积一致
SELECT b.*,bo.*
FROM beauty b
CROSS JOIN boys bo;
#sql92 和 sql99
/*
功能:sql99支持的较多
可读性:sql99实现连接条件和筛选条件的分离,可读性较高
*/
结合 WHERE 子句使用左连结
使用外连结从ShopProduct表和Product表中找出那些在某个商店库存少于50的商品及对应的商店.希望得到如下结果.
注意高压锅和圆珠笔两种商品在所有商店都无货, 所以也应该包括在内。按照”结合WHERE子句使用内连结”的思路, 我们很自然会写出如下代码
SELECT P.product_id
,P.product_name
,P.sale_price
,SP.shop_id
,SP.shop_name
,SP.quantity
FROM Product AS P
LEFT OUTER JOIN ShopProduct AS SP
ON SP.product_id = P.product_id
WHERE quantity< 50
然而不幸的是, 得到的却是如下的结果:
null值不能比较!
SELECT P.product_id
,P.product_name
,P.sale_price
,SP.shop_id
,SP.shop_name
,SP.quantity
FROM Product AS P
LEFT OUTER JOIN-- 先筛选quantity<50的商品
(SELECT *
FROM ShopProduct
WHERE quantity < 50 ) AS SP
ON SP.product_id = P.product_id
子查询
含义:
一条查询语句中又嵌套了另一条完整的select语句,其中被嵌套的select语句,称为子查询或内查询,在外面的查询语句,称为主查询或外查询。
分类:
按子查询出现的位置:
select后面:仅仅支持标量子查询
from后面:支持表子查询
- where或having后面:标量子查询(单行)、列子查询(多行)、行子查询
exists后面(相关子查询):表子查询
- 按结果集的行列数不同:
- 标量子查询(结果集只有一行一列,单行子查询)
列子查询(结果集只有一列多行,多行子查询)
行子查询(结果集有一行多列,多列多行)
表子查询(结果集一般为多行多列)
- 按结果集的行列数不同:
特点:
- 子查询都放在小括号内
- 子查询一般放在条件的右侧
- 子查询可以放在from后面、select后面、where后面、having后面、exists后面,但一般放在条件的右侧
- 子查询优先于主查询执行,主查询使用了子查询的执行结果
子查询搭配:
单行子查询 (标量子查询)
- 结果集只有一行
- 一般搭配单行操作符使用:> < = <> >= <=
- 非法使用子查询的情况:
a、子查询的结果为一组值
b、子查询的结果为空
- 多行子查询 (列子查询)
- 结果集有多行
- 一般搭配多行操作符使用:any、all、in、not in
in: 属于子查询结果中的任意一个就行 - any和all往往可以用其他查询代替
子查询就是将用来定义视图的 SELECT 语句直接用于 FROM 子句当中。其中AS studentSum可以看作是子查询的名称,而且由于子查询是一次性的,所以子查询不会像视图那样保存在存储介质中, 而是在 SELECT 语句执行之后就消失了。
标量子查询
#案例 谁的工资比 Abel 高?
SELECT *
FROM employees
WHERE salary>(
SELECT salary
FROM employees
WHERE last_name = 'Abel'
);
#案例 返回job_id与141号员工相同,salary比143号员工多的员工 姓名,job_id 和工资
SELECT last_name,job_id,salary
FROM employees
WHERE job_id = (
SELECT job_id
FROM employees
WHERE employee_id = 141
) AND salary>(
SELECT salary
FROM employees
WHERE employee_id = 143
);
#案例 查询最低工资大于50号部门最低工资的部门id和其最低工资
SELECT MIN(salary),department_id
FROM employees
GROUP BY department_id
HAVING MIN(salary)>(
SELECT MIN(salary)
FROM employees
WHERE department_id = 50
);
#案例 查询每个部门的员工个数
SELECT d.*,(
SELECT COUNT(*)
FROM employees e
WHERE e.department_id = d.`department_id`
) 个数
FROM departments d;
#案例 查询员工号=102的部门名
/*
select后面,仅仅支持标量子查询
*/
SELECT (
SELECT department_name,e.department_id
FROM departments d
INNER JOIN employees e
ON d.department_id=e.department_id
WHERE e.employee_id=102
) 部门名;
列子查询(多行子查询)
#案例 返回location_id是1400或1700的部门中的所有员工姓名
SELECT last_name
FROM employees
WHERE department_id <>ALL(
SELECT DISTINCT department_id
FROM departments
WHERE location_id IN(1400,1700)
);
行子查询(结果集一行多列或多行多列)
#案例 查询员工编号最小并且工资最高的员工信息
#方法1
SELECT *
FROM employees
WHERE employee_id=(
SELECT MIN(employee_id)
FROM employees
)AND salary=(
SELECT MAX(salary)
FROM employees
);
#方法2
SELECT *
FROM employees
WHERE (employee_id,salary)=(
SELECT MIN(employee_id),MAX(salary)
FROM employees
);
表子查询
/*
from后面
将子查询结果充当一张表,要求必须起别名
*/
#案例:查询每个部门的平均工资的工资等级
SELECT ag_dep.*,g.`grade_level`
FROM (
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id
) ag_dep
INNER JOIN job_grades g
ON ag_dep.ag BETWEEN lowest_sal AND highest_sal;
#######################################
exists关键字:
语法:
exists(完整的查询语句)
结果:
1或0
#######################################
#案例 查询有员工的部门名
#in
SELECT department_name
FROM departments d
WHERE d.`department_id` IN(
SELECT department_id
FROM employees
);
#exists
SELECT department_name
FROM departments d
WHERE EXISTS(
SELECT *
FROM employees e
WHERE d.`department_id`=e.`department_id`
);
#案例 查询没有女朋友的男神信息
#in
SELECT bo.*
FROM boys bo
WHERE bo.id NOT IN(
SELECT boyfriend_id
FROM beauty
);
#exists
SELECT bo.*
FROM boys bo
WHERE NOT EXISTS(
SELECT boyfriend_id
FROM beauty b
WHERE bo.`id`=b.`boyfriend_id`
);
关联子查询
关联子查询就是通过一些标志将内外两层的查询连接起来起到过滤数据的目的.
SELECT product_type, product_name, sale_price
FROM product AS p1
WHERE sale_price > (SELECT AVG(sale_price)
FROM product AS p2
WHERE p1.product_type = p2.product_type
GROUP BY product_type);
- 首先执行不带WHERE的主查询
- 从主查询的product _type先取第一个值=‘衣服’,通过WHERE P1.product_type = P2.product_type传入子查询,
- 从子查询得到的结果AVG(sale_price)=2500,返回主查询
- 将子查询结果再与主查询结合执行完整的SQL语句
- product _type取第二个值,得到整个语句的第二结果,依次类推
分页查询
/*
应用场景:当要显示的数据,一页显示不全,需要分页提交sql请求
语法:
select 查询列表
from 表
【join type join 表2
on 连接条件
where 筛选条件
group by 分组字段
having 分组后的筛选
order by 排序的字段】
limit 【offset,】size;
offset要显示条目的起始索引(起始索引从0开始)
size 要显示的条目个数
特点:
①limit语句放在查询语句的最后
②公式
要显示的页数 page,每页的条目数size
select 查询列表
from 表
limit (page-1)*size,size;
size=10
page
1 0
2 10
3 20
*/
#案例 查询第11条——第25条
SELECT * FROM employees LIMIT 10,15;
#案例 有奖金的员工信息,并且工资较高的前10名显示出来
SELECT *
FROM employees
WHERE commission_pct IS NOT NULL
ORDER BY salary DESC
LIMIT 10;
联合查询
/*
union:将多条查询语句的结果合并成一个结果
语法:
select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】
select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】
select 字段|常量|表达式|函数 【from 表】 【where 条件】 union 【all】
.....
select 字段|常量|表达式|函数 【from 表】 【where 条件】
应用场景:
要查询的结果来自于多个表,且多个表没有直接的连接关系,但查询的信息一致时。
对于同一个表的两个不同的筛选结果集, 使用 UNION 对两个结果集取并集, 和把两个子查询的筛选条件用 OR 谓词连接, 会得到相同的结果, 但倘若要将两个不同的表中的结果合并在一起, 就不得不使用 UNION 了.
而且, 即便是对于同一张表, 有时也会出于查询效率方面的因素来使用 UNION.
特点:★
1、要求多条查询语句的查询列数是一致的!
2、要求多条查询语句的查询的每一列的类型和顺序最好一致
3、union关键字默认去重,如果使用union all 可以包含重复项
*/
# 引入的案例:查询部门编号>90或邮箱包含a的员工信息
SELECT * FROM employees WHERE email LIKE '%a%'
UNION
SELECT * FROM employees WHERE department_id>90;
#案例 查询中国用户中男性的信息以及外国用户中年男性的用户信息
SELECT id,cname FROM t_ca WHERE csex='男'
UNION ALL
SELECT t_id,tname FROM t_ua WHERE tGender='male';