SELECT
order by 子句 (排序) ***
笔记中出现的[] 表示可以忽略的参数!
编写的格式有一个特点: order by子句 , 如果出现, 必须放在select语句的最后面!
order by 可以对查询的结果集, 指定排序的规则!
规则: 升序(asc默认排序规则)/降序(desc)
格式: order by 排序字段 [排序规则];
案例:查询所有员工的信息(id,last_name,salary) , 按照部门编号dept_id进行升序排序
语句:select id,last_name,salary,dept_id from s_emp order by dept_id asc;
练习:查询所有员工的信息(id,last_name,salary) ,按照id降序排列:
语句:select id,last_name,salary from s_emp order by id desc;
特殊情况: null在参与排序时, 是最大的!
练习:查询所有员工的信息(id,last_name,salary,manager_id), 按照领导编号(manager_id)降序排序
语句:select id,last_name,salary,manager_id from s_emp order by manager_id desc;
多字段多规则排序
格式: order by 排序字段1 规则1 ,排序字段2 规则2 … 排序字段n 规则n;
按照两个排序规则解释含义:
在排序时, 按照排序字段1 与 规则1 进行排序, 如果排序字段1的值相同 , 则按照字段2排序
案例: 按照部门编号(dept_id)降序排列 ,如果部门编号相同, 则按照员工薪资倒序排列,如果员工薪资相同, 按照员工编号(id)升序排列
select dept_id,salary,id,last_name from s_emp order by dept_id desc,salary desc,id asc;
函数
单行函数
针对每一行的数据,都会返回一个结果!
dual表格进行函数的测试
常用的字符串处理单行函数
-
upper(列名/字符串): 把字符串结果 改为全大写
案例: select upper(‘dongfei’) from dual;
-
lower(列名/字符串):把字符串结果 改为全小写
select lower(‘DongFei’) from dual; -
initcap(列名/字符串):单词首字母大写
select initcap(‘dong fei’) from dual; -
length(列名/字符串) 获取字符串的长度
select length(‘dongfei’) from dual;
select length(last_name),last_name from s_emp where id=1; -
substr(参数1,参数2,参数3):截取字符串
参数1:要被截取的字符串 或 字段名
参数2:截取的开始下标 从1开始 , 支持负数(负数表示倒数的下标)
参数3:截取的长度
select substr('1234567890',1,5) from dual; \
select substr('1234567890',0,5) from dual; /
select substr('1234567890',-5,5) from dual;
-
concat(参数1,参数2):拼接字符串
将参数1和参数2 进行拼接 并返回
select concat(‘dongfei’,‘aigaofan’) zhenai from dual; -
replace(参数1,参数2,参数3):替换字符串中的内容 *
参数1. 要被替换内容的原字符串
参数2. 在参数1中存在的要被替换的字符串片段
参数3. 替换的新的字符串片段内容
select replace(‘dongfeiaigaofan’,‘gaofan’,‘xudong’) xiaosan from dual; -
nvl(参数1,参数2) 处理空值 ***
如果参数1为空则返回参数2 , 如果参数1不为null则返回参数1;
select nvl(manager_id,0) from s_emp;
常用的处理number类型数据的单行函数:
-
round(参数1,参数2) :四舍五入 *
参数1. 要被四舍五入的数字
参数2. 妖保留的小数点后的位数 可以省略 默认值为0
select round(3.1415926,3) from dual; -
trunc(参数1,参数2): 截取数字
参数1, 要截取的数字
参数2. 要保留的小数点后的位数 ,可以省略 默认为0
select trunc(3.1415926,3) from dual; -
to_char(参数1,参数2): 格式化显示
参数1. 要被格式化的数字/列名
参数2. 格式化的模版, 是一个字符串类型的参数模板中特殊的字符: - FM: 格式的开头, 可以忽略 - L : 本地(语言环境)货币单位 - $ : 美元符号 - 9 :分为两种情况: 1. 数字9出现在小数点的后面 ,在模版中表示1-9的任意数字 (不建议) 2. 数字9出现在小数点的前面 , 在模版中表示0-9的任意数字 - 0 :分为两种情况: 1. 数字0出现在小数点的后面 ,在模版中表示0-9的任意数字 2. 数字0出现在小数点的前面,数字的前面 , 在模版中表示强制显示前导零 - . : 小数点 - , : 分隔符 案例: 查询s_emp表格中所有的salary , 并将其进行格式化显示: 格式化: 例如 2500美元应显示为: $002,500.00 格式: FM$099,999.00(建议) | FM$099,999.99(不建议) select to_char(salary,'FM$099,999.00'), id,last_name from s_emp; 注意: 在小数点的后面建议编写0 , 不要写9 !
函数的相互嵌套
函数在调用时, 都有返回值存在, 将一个函数的返回值 传递给另一个参数进行使用!
练习: 查询s_emp表格中的last_name, 并将每一个last_name最后的5个字符截取(substr)出, 以大写(upper)的方式显示出来
语句: select upper(substr(last_name,-5,5)) from s_emp;
组函数
无论有多少行的数据 ,都只返回一个结果 !
多表查询
有时我们要查询的数据, 来自于两张表 , 例如如下需求:
查询员工的信息, 得到员工的部门名称, 和员工名称以及月薪 !
此时: s_emp表格中 可以得到的信息只有如下这些 ,并不能得到部门的名称:
部门编号
员工姓名
员工月薪
还有一个表格: s_dept , 它包含了如下信息:
部门编号
部门名称
部门所在地区编号
还有一个表格:s_region , 它包含的信息:
地区编号
地区名称
内连接
多表查询基础语法: select 列名 from 表名1,表名2;
例如:查询员工表格s_emp ,s_dept,得到员工信息 和部门信息
在查询之前 回忆: 员工表人员总数25 , 部门数量:12
select * from s_emp,s_dept;
笛卡尔乘积 ***
是一种关系代数概念 , (X * Y)
在SQL中 实现的方式是 交叉连接 , 表示两个表格个三种的每一行数据任意组合 !
等值连接
使用等号作为多表查询的连接条件 , 就是等值连接
笛卡尔积问题的解决 ***
笛卡尔积 在数据库中 并没有什么用处 , 两个表格自然匹配 产生的结果 也往往不是我们想要的 .
那么这时我们 可以通过给一个多表查询语句 添加关联条件的方式(where), 来对笛卡尔乘积的结果 进行筛选 !
select * from s_emp,s_dept where dept_id=id;
上述的语句 ,出现了如下错误:
select * from s_emp,s_dept where dept_id=id
第 1 行出现错误:
ORA-00918: 未明确定义列
原因是 , 员工表(s_emp) 和 部门表(s_dept) 中都存在一个id , 那么此时我们在where后使用的id , 数据库不能明确的区分 我们所使用的是哪一个 !
可以通过在字段名称前 加入表名.的方式 来明确一个字段所属的表格:
select name,last_name,salary from s_emp,s_dept where dept_id=s_dept.id;
s_dept 部门表
- id 部门编号
- name 部门名称
- region_id 部门所在地区编号
s_region 地区表
- id 地区编号
- name 地区名称
练习:
查询部门信息: 部门编号, 部门名称, 部门所在地区名称:
select s_dept.id, s_dept.name,s_region.name from s_dept,s_region where s_dept.region_id = s_region.id;
在多表查询时, 给表格添加别名, 简化语句, 提高阅读性! ***
格式: … from 表名1 表别名1,表名2 表别名2;
练习:
查询部门信息: 部门编号, 部门名称, 部门所在地区名称:
select d.id, d.name,r.name from s_dept d,s_region r where d.region_id = r.id;
非等值连接
不使用等号作为多表查询的连接条件 , 就是非等值连接
desc salgrade;
salgrade : 工资(salary)级别(grade)表格
- grade: 工资级别 范围 1-5
- losal: 在当前级别内, 最低的范围
- hisal: 在当前级别内, 最高的范围
查询工资级别表, 观察工资级别详情:
select*from salgrade;
GRADE LOSAL HISAL
---------- ---------- ----------
1 700 1200
2 1201 1400
3 1401 2000
4 2001 3000
5 3001 9999
查询每个员工的id ,last_name,salary , 对应的工资级别 grade
-
between
select e.id,e.last_name,e.salary,s.grade from s_emp e,salgrade s where e.salary between s.losal and s.hisal; -
and
select e.id,e.last_name,e.salary,s.grade from s_emp e,salgrade s where salary>=losal and salary<=hisal;
自连接
一张表格中 , 有多层的业务含义的数据 ,要把某一层含义数据, 取出来 , 需要使用到自连接 !
例如员工表:
包含了公司所有员工的信息:
所有员工的编号
所有员工的领导编号
select id,manager_id from s_emp;
经过分析:
发现了 , s_emp表格其实存储了 两种不同的员工信息:
1. 普通员工的信息
2. 领导的信息
我们可以将s_emp表格看成两张表进行查询
例如: 寻找谁是领导:
我们将s_emp 看作两张相同的表 ,分别为员工表e , 领导表m ,
对这两张表进行查询, 关联的条件是,如果员工表中的领导编号, 与 领导表中的自身的编号相同, 则表示这个人是领导!
select m.id,m.last_name,m.salary from s_emp e,s_emp m where e.manager_id=m.id;
上述的查询语句 出现了重复, 原因在于每一个领导 不只是管理了一个普通员工, 每个普通员工都与领导匹配了一条记录, 所以数据出现了重复
所以在自连接时, 需要添加去重distinct 操作!
select distinct m.id,m.last_name,m.salary from s_emp e,s_emp m where e.manager_id=m.id;
外连接(+)
外连接的结果集, 等于自连接的结果集 加上 匹配不上的记录 !
通过(+) 来完成外连接 !
把(+)加载在where条件的字段后面 , 被(+)所修饰的字段, 对面表格的数据 全部被选中 !
先获取自连接匹配的记录, 然后在通过null去匹配对面表格的原来不匹配的数据 !
通过外连接, 完成一个特殊的需求操作:
获取不是领导的普通员工信息:
select distinct m.id,m.last_name,m.salary from s_emp e,s_emp m where e.manager_id(+)=m.id and e.id is null;
员工表 领导表
编号 姓名 领导编号 编号 姓名 领导编号
1 a null 1 a null
2 b 1 2 b 1
3 c 1 3 c 1
4 d 2 4 d 2
5 e 3 5 e 3
上面是两个表格 , 我们进行自连接的匹配,
员工表.领导编号 = 领导表.编号
2 b 1 1 a null
3 c 1 1 a null
4 d 2 2 b 1
5 e 3 3 c 1
然后把(+)放在左边员工表中 , 外连接, 结果集为 上面自连接的结果集 加上 匹配不上的记录:
匹配不上的记录:
领导表中的 :
4 d 2
5 e 3
查询的结果是一个六列的结果, 既然要把匹配不到的记录 ,放入结果中, 那就要补足前面的3列!
补足以后的 匹配不上的记录的结果为:
null null null 4 d 2
null null null 5 e 3
自连接的结果集, 加上 匹配不上的记录:
编号 姓名 领导编号 编号 姓名 领导编号
2 b 1 1 a null
3 c 1 1 a null
4 d 2 2 b 1
5 e 3 3 c 1
null null null 4 d 2
null null null 5 e 3
通过这一个结果, 我们可以清晰的看到 匹配不到的结果都是普通员工, 而在结果集中的普通员工的数据体现为: 员工表数据全为null !
SQL99 标准连接
内连接
oracle: select 列 from 表 where 关联条件 and 过滤条件
sql99: [inner] join on
格式: select 字段 from 表1 inner join 表2 on 关联条件 [where 过滤条件];
使用sql99中的语法标准, 来编写案例:
显示部门的名称, 和对应的地区名称 , 且 部门编号大于30
oracle:
select d.name,r.name from s_dept d,s_region r where d.region_id=r.id and d.id>30;
sql 99:
select d.name,r.name from s_dept d inner join s_region r on d.region_id=r.id where d.id>30;
外连接
SQL99中的左外连接: left [outer] join
概念: 左边数据一个都不能少 , 就像之前把(+)放在了右边
格式: select 字段 from 表1 left [outer] join 表2 on 连接条件 where 过滤条件;
SQL99中的右外连接:
概念: 右边数据一个都不能少 , 就像之前把(+)放在了左边
格式: select 字段 from 表1 right [outer] join 表2 on 连接条件where 过滤条件;
查询不是领导的员工 , 的id,salary,last_name
oracle:
select distinct m.id,m.salary,m.last_name from s_emp e,s_emp m where e.manager_id(+) = m.id and e.id is null;
sql 99:select distinct m.id,m.salary,m.last_name from s_emp e right outer join s_emp m on e.manager_id=m.id where e.id is null;
组函数
在使用的格式上, 单行函数与组函数基本一致 !
在查询时, 使用组函数修饰的字段产生的结果, 必须一一对应: 组函数只能产生一个结果 ,如果与普通的列一起使用, 最后产生的数据 无法一一对应, 无法组成有效的数据行, 所以会报错!
单行函数: 针对每一行数据 都会产生一个结果 !
组函数 : 针对一组数据, 只返回一个结果 , 一般是用来统计数据的!
关于null值的处理, 在组函数中 null是被忽略的!
常用的组函数:
count : 统计返回的数据量 (行数)
max : 统计返回的一组数据中的最大值
min : 统计返回的一组数据中的最小值
sum : 统计返回的一组数据的和
avg : 统计返回的一组数据的平均值
-
求出s_emp表格中的员工数量?
select count(id),last_name from s_emp;
-
求出s_emp表格中最高的员工薪资:
select max(salary) from s_emp;
-
求出s_emp表格中最低的员工薪资
select min(salary) from s_emp;
-
求出s_emp表格中员工的薪资总和
select sum(salary) from s_emp;
-
求出s_emp表格中的员工的薪资平均值
select avg(salary) from s_emp;
关于null值被忽略的操作:
-
求出所有员工提成的平均值:
select avg(commission_pct) from s_emp;
上述的语句查询的结果为13 , 经过分析, 发现avg组函数 忽略了null值的存在, 将五个拥有提成的用户的提成值相加, 除以了5得出的平均值 ;
如果要计算包含null数据的平均值, 怎么办? , 可以手动求和, 然后除以总数
select sum(commission_pct)/count(*) from s_emp;
作业:
-
显示员工的id,last_name,salary,部门名称 , 要求, 把没有部门编号的员工也给查询到:
前置条件:
公司业务需要, 把员工编号为23 24 25 的员工 分配到了新的部门, 但是部门还未成立! update s_emp set dept_id = null where id in(23,24,25); commit;
-
显示每个员工的工资 和对应的工资级别 , 超出工资范围的 也要显示出来 :
工资表: salgrade
前置条件:
公司老板觉得自己的工资太高了, 不合适 , 给自己降了-10000的工资 ! 还让自己的秘书 以身作则 , 一起降了工资 ! update s_emp set salary=12500 where id=1; update s_emp set salary=500 where id=22; commit;
如果在外连接时, 使用between 需要在最小范围后面加入(+) , 最大范围后面加入(+)
salary between losal(+) and hisal(+)
-
显示每个部门的名称 和 对应的地区的名称, 没有地区编号的也要显示出来 :
前置条件:
因为业务需要, 增加了新的部门, 但是还没有确定办公的地点:insert into s_dept values(99,'chiji',null); commit;