数据查询 涉及到DQL(Data Query Language)是sql语句的一类
本文全面介绍了mysql下 select 语句的各种查询方式:普通查询,模糊查询,查询排序,分页查询,聚合函数查询 ,分组查询,子查询,连接查询(内连接 外连接) 组合查询。
每一条 select 语句都经过测试,没嘛哒!
创建两个表作为实验对象:
create table dept
(
deptno int primary key, -- 部门号
dname nvarchar(30), -- 部门名称
loc nvarchar(30) -- 部门所在地点
);
create table emp
(
empno int primary key , -- 员工编号
ename nvarchar(30), -- 员工姓名
job nvarchar(30), -- 员工职位
mgr int, -- 员工领导的员工编号
hiredate datetime, -- 入职日期
sal numeric(10,2), -- 薪水
comm numeric(10,2), -- 奖金
deptno int foreign key references dept(deptno) -- 员工所在部门的编号 与部门表通过外键关联
);
在表中插入相应的数据
insert into dept values(10,'accounting','newyork');
insert into dept values(20,'research','dallas');
insert into dept values(30,'sales','chicago');
insert into dept values(40,'operations','boston');
insert into emp (empno,ename,job,mgr,hiredate,sal,deptno)values(7934,'miller','clerk',7782,'1982-1-23',1300.00,10);
insert into emp (empno,ename,job,mgr,hiredate,sal,deptno)values(7369,'smith','clerk',7902,'1980-12-17',800.00,20);
insert into emp (empno,ename,job,mgr,hiredate,sal,deptno)values(7566,'jones','manager',7839,'1981-4-2',2975.00,20);
insert into emp (empno,ename,job,mgr,hiredate,sal,deptno)values(7698,'blake','manager',7839,'1981-5-1',2850.00,30);
insert into emp (empno,ename,job,mgr,hiredate,sal,deptno)values(7782,'clark','manager',7839,'1981-6-9',2450.00,10);
insert into emp (empno,ename,job,mgr,hiredate,sal,deptno)values(7788,'scott','analyst',7566,'1987-4-19',3000.00,20);
insert into emp (empno,ename,job,mgr,hiredate,sal,deptno)values(7876,'adams','clerk',7788,'1987-5-23',1100.00,20);
insert into emp (empno,ename,job,mgr,hiredate,sal,deptno)values(7900,'james','clerk',7698,'1981-12-3',950.00,30);
insert into emp (empno,ename,job,mgr,hiredate,sal,deptno)values(7902,'ford','analyst',7566,'1981-12-3',3000.00,20);
insert into emp (empno,ename,job,hiredate,sal,deptno)values(7839,'king','president','1981-11-17',5000.00,10);
insert into emp values(7499,'allen' ,'salesman',7698,'1981-2-20',1600.00,300.00 ,30);
insert into emp values(7521,'ward' ,'salesman',7698,'1981-2-22',1250.00,500.00 ,30);
insert into emp values(7844,'turner','salesman',7698,'1981-9-8' ,1500.00,0.00 ,30);
insert into emp values(7654,'martin','salesman',7698,'1981-9-28',1250.00,1400.00,30);
查询语句————> select 语句的结构分析:
select distinct [字段名] from [(左)表名] [连接类型] join [(右)表名] on [连接条件] where [查询条件] group by [字段名(按该字段分组)] having [分组删选条件] order by [字段名(按该字段排序)] [asc/desc(排序方式)] limit [分页条件]
select语句执行顺序:
-- (7) SELECT
-- (8) DISTINCT <select_list>
-- (1) FROM <left_table>
-- (3) <join_type> JOIN <right_table>
-- (2) ON <join_condition>
-- (4) WHERE <where_condition>
-- (5) GROUP BY <group_by_list>
-- (6) HAVING <having_condition>
-- (9) ORDER BY <order_by_condition>
-- (10) LIMIT <limit_number>
-- 1、 FORM: 对FROM左边的表和右边的表计算笛卡尔积,产生虚表VT1。
-- 2、 ON: 对虚表VT1进行ON过滤,只有那些符合<join-condition>的行才会被记录在虚表VT2中。
-- 3、 JOIN: 如果指定了OUTER JOIN(比如left join、 right join),那么保留表中未匹配的行就会作为外部行添加到虚拟表VT2中,产生虚拟表VT3。
-- 4、 WHERE: 对虚拟表VT3进行WHERE条件过滤。只有符合<where-condition>的记录才会被插入到虚拟表VT4中。
-- 5、 GROUP BY: 根据group by子句中的列,对VT4中的记录进行分组操作,产生VT5。
-- 6、 HAVING: 对虚拟表VT5应用having过滤,只有符合<having-condition>的记录才会被 插入到虚拟表VT6中。
-- 7、 SELECT: 执行select操作,选择指定的列,插入到虚拟表VT7中。
-- 8、 DISTINCT: 对VT7中的记录进行去重。产生虚拟表VT8.
-- 9、 ORDER BY: 将虚拟表VT8中的记录按照<order_by_list>进行排序操作,产生虚拟表VT9.
-- 10、 LIMIT: 取出指定行的记录,产生虚拟表VT10, 并将结果返回。
说明:网上给出的资料大都是这个顺序,但本人觉得应将 2,3 的顺序调换,尤其涉及到左连接和右连接的时候,根据连接类型,使用连接条件:若是内连接,排除不匹配的行;若是左连接,保留左表数据,排选右表数据;右连接则反之。(个人理解,大家多加批评指正)。
查询从这里开始:
普通查询
-- 从表中查询所有记录 ,*表示在结果中显示全部字段信息 。
select * from emp;
-- 从 emp 表中查询 sal, job , deptno 三个字段。
select sal,job,deptno from emp;
-- distinct 关键字 表示重复的结果只显示一条。
select distinct deptno from emp;
-- 查询员工名称和其年工资, comm为NULL是用 0 计算。对IFNULL(exp1,exp2)函数,如果exp1的值为NULL 则显示exp2;否则显示exp1.
select ename,sal*13+IFNULL(comm,0)*13 as '年工资' from emp;
-- 显示在 1982-1-1 之后入职的人员 。where 关键字后跟查询条件。
select *from emp where hiredate >'1982-1-1';
-- 显示工资 sal 在2000 和 3000 之间 ( between and ) 。
select *from emp where sal between 2000 and 3000;
-- 查询最高的领导- 最高领导之上再没有领导 所以 mgr 字段为null.
select *from emp where mgr is null;
模糊查询
-- 查询ename 是 字母 j 开头 同时要么是管理员 要么 sal 在 500 以上
-- like 后面的条件是一个模糊查询,'%' 匹配多个字符,'_'匹配一个字符 ,or 表示选择条件查询——或者
select *from emp where (sal > 500 or job = 'manager' ) and ename like 'j%';
查询排序
-- 按员工薪资排序
-- order by sal 表示按照 sal 来排序, desc 表示降序,换成 asc 便表示升序
select *from emp order by sal desc;
-- 按照部门号的升序而员工工资的降序排列
select * from emp order by deptno asc ,sal desc;
分页查询:limit
-- mysql 提供了 关键字 limit 可以实现分页查询, limit 后面可以跟 两个数字作为参数,第一个数字表示从第几行起开始查询,
-- 第二个数字表示查询多少条记录,当记录数小于该数字时全部显示。当第一个数字为 0 (零)时可以略去不写。下面这两条查询语句的结果是一样的。
select * from emp limit 4;
select * from emp limit 0,4;
聚合函数查询: 聚合函数最大的特点就是能够根据一组数据求出一个值
-- 查询平均工资和工资总和
select avg(sal) 平均工资, sum(sal) 总工资 from emp;
-- 查询工资高于平均工资的员工姓名-- 这里用到了子查询(下面有讲)
select ename , sal,(select avg(sal) from emp) 平均工资 from emp where sal > (select avg(sal) from emp);
分组查询: group by
-- 查询每个部门的平均工资和最高工资
select avg(sal) ,max(sal),deptno from emp group by deptno;
-- 显示每个部门的每种岗位的平均工资 按照部门号 和 职位 两个字段进行分组
-- mysql 会先按照 deptno进行分组,然后在每一个组中再按 job 分组
select avg(sal) , min(sal) ,deptno , job from emp group by deptno ,job order by deptno;
要求:显示平均工资低于两千的部门号 和它的平均工资
-- 第一步: 查询每个部门的平均工资,并显示它的部门号
-- 第二步:将其中平均工资低于 2000 的结果删选出来 having vv < 2000;
-- 说明: having 往往和group by 结合使用, 可以分组查询结果进行筛选
-- having 子句往往包含聚合函数(所谓聚合函数,其最大的特点就是能够根据一组数据求出一个值)
-- where 也是对结果进行筛选,但where子句中不能包含聚合函数,where子句也往往是在分组和聚合运算之前
-- 对输入行进行筛选,可以作用于表或视图
-- having子句是在分组和聚合运算之后对其结果进行筛选,作用于组。这里 vv 只是取了一个别名。
select avg(sal) vv , deptno from emp group by deptno having vv > 2000 order by vv asc;
子查询
-- 嵌入在其他sql语句中的select语句
-- where 语句使用子查询
-- 要求:显示与Smith同一部门的员工 (在where 语句中使用子查询)
-- 若子查询返回的是多行结果,= 号就要改成关键字 in
-- 当然 根据需求 还可以使用 not in 或其他比较运算符:= <> < > != ....(当然,若子查询返回结果是多行,则不能使用比较运算符)
select *from emp where deptno=(select deptno from emp where ename='smith');
-- 显示其他部门中与10号部门职位一样的员工信息
select * from emp where job in(select job from emp where emp.deptno='10') and deptno <> '10';
-- 带 exists 关键字的子查询
-- 使用 exists 关键字时, 内层查询语句不返回查询的记录,而是根据是否有记录满足查询条件返回一个真价值(true/false)
-- 当返回值为 true 时外层查询语句正常执行,否则外层查询语句不执行查询或查询不出任何记录;
-- not exists 的作用于exists 刚好相反
select * from emp where exists (select ename from emp where empno = '7777');
-- 带 any 的子查询
-- any 表示任意一个
-- 它允许创建一个表达式,对子查询的返回值列表,进行比较,只要满足内层子查询中的,任意一个比较条件,就返回一个结果作为外层查询条件
-- 如果将 any 换成 all 情况就刚好相反,就要满足全部的条件才行
select * from emp where sal >= any(select avg(sal) from emp group by deptno);
在 from 语句中使用子查询
-- 要求: 显示高于部门平均工资的员工信息
-- 第一步:查询每个部门的平均工资:select avg(sal) avgsal,deptno from emp e group by deptno
-- 第二步:将上面的查询结果作为临时表,利用内连接(下面有讲)
select * from emp e natural join (select avg(sal) avgsal,deptno from emp e group by deptno) temp where e.sal > temp.avgsal;
select * from emp e inner join
(select avg(sal) avgsal,deptno from emp e group by deptno) temp
on e.deptno=temp.deptno where e.sal > temp.avgsal;
连接查询: 主要有内连接和外连接(内连接、外连接都是在交叉连接的基础上根据连接条件删选的)
内连接 内联接使用比较运算符根据构成连接的表共有的列的值匹配它们的行,不匹配的行将被排除
-- cross join 叫做交叉连接 加上限制条件e.deptno=d.deptno 便是等同连接, 等同连接又叫作 等值连接 是内连接最常见的例子;以下四个 sql 语句是等价的
-- count(*) 是一个聚合函数,表示求记录数
select count(*) from emp e cross join dept d where e.deptno=d.deptno ;
select count(*) from emp e , dept d where e.deptno=d.deptno ;
select count(*) from emp e inner join dept d where e.deptno=d.deptno ;
select count(*) from emp e inner join dept d on e.deptno=d.deptno ;
自然连接
-- 自然连接无需指定连接列,SQL会检查两个表中是否相同名称的列,
-- 且直接在连接条件中使用,并且在连接条件中仅包含一个连接列。不允许使用ON语句
select e.ename,d.dname from emp e natural join dept d;
自连接
-- 显示每个员工的姓名和他的上级领导的名字
-- 自连接 就是在需要的时候将一张表看作两张,进行自己的两个字段匹配
select worker.ename as worker, boss.ename as boss from emp worker inner join emp boss on worker.mgr=boss.empno;
左连接(外连接:左连接,右连接)
-- 上面的自连接实现中 对于员工 king 没有匹配的内容,所以查询结果是 13 条记录
-- 与内连接不同,左外连接还返回左表中不符合连接条件但却符合查询条件的数据行,也就是说左边表显示满足查询条件所有数据,右边表数据数据少了补NULL值,数据多了不显示;关键字outer 可以略去。
select worker.ename as worker, boss.ename as boss from emp worker left outer join emp boss on worker.mgr=boss.empno;
右连接: 与左连接刚好对称。
组合查询 : 通过组合查询可以实现传说中的全连接
-- union 关键字用于将多条 select 语句返回的结果集合并成一个结果集
-- union 将所有的查询结果合并到一起,排除重复记录,并默认排序
-- union all 将所有的查询结果合并到一起,保留重复记录,不排序
-- 组合查询 多用于以下情况:
-- 1.从多个表中查询出相似结构的数据,并且返回一个结果集
-- 2.从单个表中多次SELECT查询,将结果合并成一个结果集返回。注意:排序的字段前面不能带表名,并且是两个select语句中共有的字段
(select ee.empno,ee.ename,ee.job,ee.sal, ee.comm from emp ee where ee.sal > 1500)
union
(select e.empno,e.ename,e.job,e.sal,e.comm from emp e where e.comm is not null) order by sal;
-- 上面的两条select 语句通过 union 组合, 将结果合并输出。这条组合查询也可用 where 条件来完成。
select e.empno,e.ename,e.job,e.sal, e.comm from emp e where( e.sal > 1500) or (e.comm is not null);
组合查询与 where
-- 一般: 能用where来实现的都可以用 union 来实现,而能用 union 实现的where却不一定能实现 例如 用union all 合并后会有重复记录
-- 1. union 必须由两条或更多的 select 语句组成,语句之间用 union 连接
-- 2. union 中的每个select语句必须包含相同的列、表达式或聚合函数,它们的出现顺序可以不一致(这里指查询相同的字段,表可以不一样)
-- 3. 列的数据类型必须相同(或兼容,兼容不提倡)
组合查询排序
-- 1. 只能使用一条 order by 子句,并且只能放在最后一条 select 语句后面
-- 2. 必须使用共同的字段名
-- 3. 不能带表名,哪怕表名相同
(select empno,ename as '名字',job,sal, comm from emp where sal > 1500)
union
(select empno,ename,job,sal,comm from emp where comm is not null) order by sal;
-- 多表组合查询时 select 语句中显示的字段名可能不一样 但显示的结果却是第一条 select 语句中的字段名
-- 可以通过 在 sql 语句中加入 '表名的别名' as tableName 提供新的一列 区分数据来自 哪张表.
(select empno,ename as '名字',job,sal, comm ,'first' as tableName from emp where sal > 1500)
union
(select empno,ename,job,sal,comm, 'second' as tableName from emp where comm is not null) order by sal;