Oracle (02)SELECT.函数.多表查询.笛卡尔乘积.SQL99 标准连接.组函数.

版权声明:cxyo.cn https://blog.csdn.net/weixin_44075537/article/details/85032808

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表格进行函数的测试

常用的字符串处理单行函数

  1. upper(列名/字符串): 把字符串结果 改为全大写

    案例: select upper(‘dongfei’) from dual;

  2. lower(列名/字符串):把字符串结果 改为全小写
    select lower(‘DongFei’) from dual;

  3. initcap(列名/字符串):单词首字母大写
    select initcap(‘dong fei’) from dual;

  4. length(列名/字符串) 获取字符串的长度
    select length(‘dongfei’) from dual;
    select length(last_name),last_name from s_emp where id=1;

  5. 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;
  1. concat(参数1,参数2):拼接字符串
    将参数1和参数2 进行拼接 并返回
    select concat(‘dongfei’,‘aigaofan’) zhenai from dual;

  2. replace(参数1,参数2,参数3):替换字符串中的内容 *

    参数1. 要被替换内容的原字符串
    参数2. 在参数1中存在的要被替换的字符串片段
    参数3. 替换的新的字符串片段内容
    select replace(‘dongfeiaigaofan’,‘gaofan’,‘xudong’) xiaosan from dual;

  3. nvl(参数1,参数2) 处理空值 ***

    如果参数1为空则返回参数2 , 如果参数1不为null则返回参数1;
    select nvl(manager_id,0) from s_emp;

常用的处理number类型数据的单行函数:

  1. round(参数1,参数2) :四舍五入 *

    参数1. 要被四舍五入的数字
    参数2. 妖保留的小数点后的位数 可以省略 默认值为0
    select round(3.1415926,3) from dual;

  2. trunc(参数1,参数2): 截取数字

    参数1, 要截取的数字
    参数2. 要保留的小数点后的位数 ,可以省略 默认为0
    select trunc(3.1415926,3) from dual;

  3. 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

  1. 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;

  2. 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     : 统计返回的一组数据的平均值
  1. 求出s_emp表格中的员工数量?

    select count(id),last_name from s_emp;

  2. 求出s_emp表格中最高的员工薪资:

    select max(salary) from s_emp;

  3. 求出s_emp表格中最低的员工薪资

    select min(salary) from s_emp;

  4. 求出s_emp表格中员工的薪资总和

    select sum(salary) from s_emp;

  5. 求出s_emp表格中的员工的薪资平均值

    select avg(salary) from s_emp;

关于null值被忽略的操作:

  1. 求出所有员工提成的平均值:

    select avg(commission_pct) from s_emp;

    上述的语句查询的结果为13 , 经过分析, 发现avg组函数 忽略了null值的存在, 将五个拥有提成的用户的提成值相加, 除以了5得出的平均值 ;

    如果要计算包含null数据的平均值, 怎么办? , 可以手动求和, 然后除以总数

    select sum(commission_pct)/count(*) from s_emp;

作业:

  1. 显示员工的id,last_name,salary,部门名称 , 要求, 把没有部门编号的员工也给查询到:

    前置条件:

    公司业务需要, 把员工编号为23 24 25 的员工 分配到了新的部门, 但是部门还未成立! 
    
    update s_emp set dept_id = null where id in(23,24,25);
    commit; 
    
  2. 显示每个员工的工资 和对应的工资级别 , 超出工资范围的 也要显示出来 :

    工资表: 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(+)
    
  3. 显示每个部门的名称 和 对应的地区的名称, 没有地区编号的也要显示出来 :

    前置条件:
    因为业务需要, 增加了新的部门, 但是还没有确定办公的地点:

    insert into s_dept values(99,'chiji',null);
    commit;
    

猜你喜欢

转载自blog.csdn.net/weixin_44075537/article/details/85032808