DQL
目录:
基础语法:
SELECT
[DISTINCT] 查询项 [[as] 别名], 查询项...
[FROM
表名]
[JOIN_TYPE JOIN
表2
ON 连接条件]
[WHERE
筛选条件]
[GROUP BY
分组项
[HAVING
分组后的条件筛选]]
[ORDER BY
排序的字段|表达式|函数|别名 [ASC|DESC]]
[LIMIT [offset,] size];
注:
- SQL索引都是从1开始,与编程语言不同(原因很简单,查出来3条数据,不能说第0条是xxx吧)
但limit的offset是从0开始的,offset表示要显示条目的起始索引,size表示要显示的条目个数 - 通过select查询完的结果集(其实每个子句执行后的结果集都是),是一个虚拟的表,不是真实存在
- 执行顺序:from表 -> join连接表 -> on连接条件 -> where筛选条件 -> group by分组 -> having分组后筛选 -> select查询 -> order排序 -> limit
即先看表存不存在,再看查询条件,然后查询,最后排序 - 查询项
- 结果去重 distinct
- 查询项可以是字段,也可以是常量值、表达式、函数
- 查询项可以用``括起来,以区分字段名和关键字,也可以不用
- 查询项可以用 AS 或 空格 起别名, 别名中若包含特殊符号,如空格,#等,需要用引号括起来
- where 条件:
- 按条件: > , < ,=(注意等于是单个=), >= , <= , !=, <>(表示不等于)
- 按逻辑条件:and(&&), or(||), not(!)
- 模糊查询: like, between and(包含边界), in, is null, is not null
- like 通常与通配符搭配使用:
% 匹配任意多个字符,包含0个字符
_ 匹配任意单个字符
like支持转义: LIKE '_\_%' 表示匹配第二个字符为_的字符串
另一种转义方式:LIKE '_$_%' ESCAPE '$'; - in
in列表的值类型必须一致或兼容
in列表中不支持通配符
- like 通常与通配符搭配使用:
- order by 排序:
- order by后可以有多个排序字段,按先后顺序:
order by salary desc, age asc; //按工资降序排序,若工资一样,则按年龄升序排序 - order by 语句通常是放在最后面,(limit子句前)
- order by后可以有多个排序字段,按先后顺序:
函数
1.单行函数
ifnull(字段) 如果字段为null,就返回1, 否则返回0
ifnull(字段, val) 如果字段为null,则转换成val
1、字符函数
concat(str, ... , str) 字符串拼接
(注意:mysql中的 + 不能做拼接,只能做运算:
①两个操作数都为数值型,则做加法运算;
②只要其中一方为字符型,试图将字符型转换成数值型,如果转换成功,则继续做加法运算,如果转换失败,则将字符型转换成0,在做加法运算;
③只要其中一方为null,则结果肯定为null)
length(str) 获取字节个数
(注意:一个汉字占2个字节(gbk)或3个字节(utf8))
substr(str, startIndex [, len]); 截取子串,注意:startIndex从1开始
instr(str, substr) 返回子串第一次出现的索引,没有返回0
replace(str, srcstr, dststr) 替换,将str中所有的srcstr替换成dststr
upper(str) 转换成大写
lower(str) 转换成小写
trim(str) 去前后指定的空格和字符
//select trim('a' FROM 'aaa张三丰aaa') as 'out';去除字符串前后的a,输出为“张三丰”
ltrim() 去左边空格
rtrim() 去右边空格
lpad(str, len, padchar) 左填充,若str长度小于len,则用padchar进行填充,若小于len,则只显示len长度的str
rpad() 右填充
2、数学函数
mod() 取余
//mod(a, b) = a - a/b*b;
rand() 随机数
floor() 向下取整
ceil() 向上取整
round() 四舍五入
//round(1.567) //结果是2
//round(1.567, 2) //小数点后保留2位,结果是1.57
truncate() 截断,小数点后保留几位
//truncate(1.567, 2) //结果是1.56
3、日期函数
now() 当前系统日期+时间
curdate() 当前系统日期
curtime() 当前系统时间
str_to_date(str, format) 将字符转换成日期
//正常解析 str_to_date('1999-9-1')
//按指定格式解析:str_to_date('9-1-1999', '%m-%d-%Y');结果是1999-09-01
date_format(date, format) 将日期转换成字符
//date_format(now(), '%Y年%m月%d日');结果是2019年11月1日
//获取指定部分
//eg: YEAR(NOW()); 即可获取年
datediff(date1, date2) 可以获取两个日期相差的天数
//eg: datediff('2018-12-01', now()); 查询从2018年12月1日到现在有多少天
4、流程控制函数
if(statement, expr1, expr2) 处理双分支,
等效于 statement ? expr1 : expr2;
case语句 处理多分支
情况1:处理等值判断
case 字段
when 值1 then expr1
when 值2 then expr2
...
else expr_default
end as 别名
情况2:处理条件判断
case
when 条件1 then expr1
when 条件2 then expr2
...
else expr_default
end as 别名
5、其他函数
version() 版本
database() 当前库
user() 当前连接用户
2.分组函数(聚合函数,用于统计)
count() 计数
sum() 求和
max() 最大值
min() 最小值
avg() 平均值
注:
- count的参数可以支持:
字段、*、常量值(一般是1)
count(字段): 统计的是该字段不为null的行数
count(*):统计的是所有行数,(因为*表示所有列,而一行里面不可能所有字段都是null)
count(1): 相当于加了一列,该列全为1,因此统计的也是所有行数,在innodb引擎下,与count(*)的效率差不多。 - 以上五个分组函数计算时都不计null值,除了count(*)
- sum和avg一般用于处理数值型
max、min、count可以处理任何数据类型 - 都可以搭配distinct使用,用于统计去重后的结果, distinct写在函数内
eg: count(distinct age); 统计不同年龄的个数 - 和聚合函数一起查询的字段都是group by 后的字段
查询
分组查询
分组查询一般与分组函数一起使用
SELECT
分组函数,字段(这里的字段是group by后的字段)
FROM
表名
[WHERE
分组前的条件筛选]
GROUP BY
字段 (按什么进行分组)
[HAVING
分组后的条件筛选]
[ORDER BY
排序的字段|表达式|函数|别名 [ASC|DESC]]
注:
- 分组函数后面不能跟普通字段,可以是group by后的字段
- 为性能应尽量用分组前筛选,即where,但有的必须用分组后筛选
条件筛选分类 | 数据源 | 位置 | 关键字 |
---|---|---|---|
分组前筛选 | 原始表 | group by子句前 | where |
分组后筛选 | 分组查询后的结果集 | group by子句后 | having |
- 不仅可以按字段进行分组,还可以是 表达式、函数、别名
连接查询(又叫多表查询或多表连接)
- 笛卡尔积:表1 有m行,表2有n行,结果=m*n行
发生原因:没有有效的连接条件
如何避免:添加有效的连接条件 - 分类:
- 按标准分类:
sql92标准:仅仅支持内连接
- 按标准分类:
select 查询列表
from 表1 别名 [连接类型], 表2 别名
where 连接条件 and 筛选条件
[group by 分组]
[having 筛选条件]
[order by 排序列表]
sql99标准【推荐】:支持内连接+外连接(左外和右外)+交叉连接
select 查询列表
from 表1 别名
[连接类型] join 表2 别名
on 连接条件
[where 筛选条件]
[group by 分组]
[having 筛选条件]
[order by 排序列表]
- 按功能分类:
- 1.内连接:[inner] 找交集,即两张表都有的记录
1.1等值连接
1.2非等值连接
1.3自连接
- 2.外连接:
- 应用场景:用于查询一个表中有,另一个表没有的记录
- 外连接查询结果 = 内连接结果 + 主表中有而从表没有的记录
外连接的查询结果为主表中的所有记录:
如果从表中有和它匹配的,则显示匹配的值
如果从表中没有和它匹配的,则显示null
2.1左外连接 left [outer] 左边的是主表
2.2右外连接 right [outer] 右边的是主表
左外和右外交换两个表的顺序,可以实现同样的效果
2.3全外连接 full [outer] 【MySQL不支持全外连接】
全外连接 = 内连接的结果 + 表1中有但表2没有的 + 表2中有但表1没有的
- 3.交叉连接 cross
交叉连接查询结果是笛卡尔积的结果
1.1 等值连接
- 连接条件用 = ,n表连接,至少需要n-1个连接条件
- 多表等值连接的结果为多表的交集部分
- 多表的顺序没有要求
- 一般需要为表起别名(简洁性和区分多个表中重名的字段),注意起了别名就只能用别名了
#查询员工名、工种号、工种名
//sql92
SELECT e.last_name,e.job_id,j.job_title
FROM employees e,jobs j
WHERE e.`job_id`=j.`job_id`;
//sql99
SELECT last_name,job_title
FROM employees e
INNER JOIN jobs j
ON e.`job_id`= j.`job_id`;
1.2 非等值连接
- 与等值连接不同的地方在于连接条件中不是用 = 作为两个表的连接条件,比如用 between and
#案例1:查询员工的工资和工资级别
SELECT salary,grade_level
FROM employees e,job_grades g
WHERE salary BETWEEN g.`lowest_sal` AND g.`highest_sal`;
1.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`;
子查询
- 概念:
出现在其他语句中的select语句,称为子查询或内查询
外部的查询语句,称为主查询或外查询 - 分类:
-
按结果集的行列数不同:
标量子查询(结果集只有一行一列)
列子查询(结果集只有一列多行)
行子查询(结果集有一行多列)
表子查询(结果集一般为多行多列) -
按子查询出现的位置:
- select后面:
仅仅支持标量子查询 - from后面:
支持表子查询 - where或having后面:
标量子查询(单行) √
列子查询 (多行) √
行子查询 - exists后面(相关子查询)
标量子查询
列子查询
行子查询
表子查询
- select后面:
-
- 特点:
①子查询放在小括号内
②子查询一般放在条件的右侧
③标量子查询,一般搭配着单行操作符使用
> < >= <= = <>
列子查询,一般搭配着多行操作符使用
in、any/some、all
④子查询的执行优先于主查询执行,主查询的条件用到了子查询的结果
一、where或having后面的子查询
- 标量子查询
#查询最低工资大于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
);
- 列子查询
#返回其它部门中比job_id为‘IT_PROG’部门所有工资都低的员工的员工号、姓名、job_id 以及salary
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary<ALL(
SELECT DISTINCT salary
FROM employees
WHERE job_id = 'IT_PROG'
) AND job_id<>'IT_PROG';
#或
SELECT last_name,employee_id,job_id,salary
FROM employees
WHERE salary<(
SELECT MIN( salary)
FROM employees
WHERE job_id = 'IT_PROG'
) AND job_id<>'IT_PROG';
- 行子查询
#查询员工编号最小并且工资最高的员工信息
#标量子查询
SELECT *
FROM employees
WHERE employee_id=(
SELECT MIN(employee_id)
FROM employees
)AND salary=(
SELECT MAX(salary)
FROM employees
);
#行子查询
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后面的子查询(相关子查询)
#查询有员工的部门名
#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`
);
四、案例
# 查询平均工资最低的部门信息
#方式一:
#①各部门的平均工资
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
#②查询①结果上的最低平均工资
SELECT MIN(ag)
FROM (
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id
) ag_dep
#③查询哪个部门的平均工资=②
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary)=(
SELECT MIN(ag)
FROM (
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id
) ag_dep
);
#④查询部门信息
SELECT d.*
FROM departments d
WHERE d.`department_id`=(
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary)=(
SELECT MIN(ag)
FROM (
SELECT AVG(salary) ag,department_id
FROM employees
GROUP BY department_id
) ag_dep
)
);
#方式二:
#①各部门的平均工资
SELECT AVG(salary),department_id
FROM employees
GROUP BY department_id
#②求出最低平均工资的部门编号
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1;
#③查询部门信息
SELECT *
FROM departments
WHERE department_id=(
SELECT department_id
FROM employees
GROUP BY department_id
ORDER BY AVG(salary)
LIMIT 1
);
分页查询
格式:
limit [offset,] size;
注:
- limit语句放在查询语句的最后,也是最后执行的
- offset要显示条目的起始索引(起始索引从0开始), offset = (page-1)*size
- size 要显示的条目个数
联合查询
概念:
将多条查询语句的结果合并成一个结果
语法:
查询语句1
union
查询语句2
应用场景:
要查询的结果来自于多个表,且多个表没有直接的连接关系,但查询的信息一致时
特点:
- 要求多条查询语句的查询列数是一致的!
- 要求多条查询语句的查询的每一列的类型和顺序最好一致
- union默认去重,
如果使用union all,则可以包含重复项