ORACLE复杂查询

1 条件逻辑

1.1 逻辑判断和条件判断

在Oracle数据库中,逻辑判断和条件判断是两个密切相关但又不完全相同的概念。

  • 逻辑判断

逻辑判断主要关注的是根据逻辑运算符(如AND、OR、NOT)对条件表达式的结果进行逻辑运算,从而得出最终的布尔值(true或false)。

  • 条件判断

条件判断则更侧重于根据给定的条件或表达式来判断某个操作是否应该执行,或者应该执行哪个分支的操作。

1.2 CASE...WHEN语句

用于执行条件逻辑,类似于其他编程语言中的 if-else 结构。

1.2.1 简单CASE

简单 CASE 表达式比较一个表达式的值与一组简单的表达式以确定结果。

  • 语法:

case expression

when value1 then result1

when value2 then result2

...

else default_result

end

  • 示例:

    select empno,ename,case job
    when 'PRESIDENT' then '总裁辛苦了'
    when 'MANAGER' then '经理好'
    when 'ANALYST' then '组长干的不错'
    when 'SALESMAN' then '跑业务去'
    when 'CLERK' then '嗯'
    else '保安'
    end as 问候语
    from emp;

1.2.2 搜索CASE

搜索 CASE 表达式允许更复杂的条件判断

  • 语法:

case

when expression1 then result1

when expression then result2

...

else default_result

end

  • 示例:

    select empno,ename,case
    when sal<1500 then 'Low'
    when sal between 1500 and 2000 then 'Medium'
    when sal>2000 then 'High'
    else 'Unknown'
    end as salary_grade
    from emp;

2 分组函数

分组函数,也叫聚合函数,作用于一组数据,并对一组数据返回一个值。

通常和分组查询连用,但不能和非分组字段连用!

2.1 count函数

返回找到的记录数

  • count(*):全列统计,统计时不会忽略null,推荐使用

    --统计每个部门的员工数量
    select deptno,count(*) from emp group by deptno;

  • count(1):常量统计,统计时不会忽略null

    --统计每个部门的员工数量
    select deptno,count(1) from emp group by deptno;

  • count(col):指定列统计,统计时忽略null

    --统计每个部门拥有佣金的员工的数量
    select deptno,count(comm) from emp group by deptno;

2.2 min函数

返回一个数字列或计算列的最小值

复制代码
--最低工资
select min(sal) from emp;

2.3 man函数

返回一个数字列或计算列的最大值

复制代码
--最高工资
select max(sal) from emp;

2.4 avg函数

返回一个数字列或计算列的平均值

复制代码
--平均工资
select avg(sal) from emp;

2.5 sum函数

返回一个数字列或计算列的和

复制代码
--工资支出
select sum(sal) from emp;

3 分组查询

3.1 group by语句

通过一定的规则将一个数据集划分成若干个小的区域,然后针对若干个小区域进行数据处理。

在查询的过程中需要按某一列的值进行分组,以统计该组内数据的信息时,就要使用group by子句。

group by子句一定要与分组函数结合使用,否则没有意义。

通常不与where联用,条件语句使用having关键字

  • 示例:

    --每个部门的员工人数
    select deptno,count() from emp group by deptno;
    --每个部门的员工的平均工资
    select deptno,avg(nvl(sal,0)) from emp group by deptno;
    --求出某个部门中相同职位的员工人数
    select deptno,job,count(
    )
    from emp
    group by deptno,job
    order by deptno;

3.2 having 语句

用在group by子句后,表示分组要遵循的条件。

HAVING 子句对 GROUP BY 子句设置条件的方式与WHERE 子句和 SELECT 语句交互的方式类似。

WHERE 子句搜索条件在进行分组操作之前应用,而HAVING 搜索条件在进行分组操作之后应用。

HAVING 子句可以引用选择列表中出现的任意项,可以包含聚合函数
having子句通常与group by子句结合使用

  • 示例:

    --部门的员工人数大于5的部门编号
    select deptno,count()
    from emp
    group by deptno
    having count(
    )>5;

4 子查询

子查询其实是指嵌入在其他SQL语句中的SELECT语句,也称为嵌套查询。

使用子查询注意事项:

子查询可以嵌套多层

子查询需要圆括号()括起来

4.1 单行单列子查询

只返回一行结果的子查询,使用"="进行条件连接

复制代码
--查询SMITH所在部门人员的信息
select * from emp where empno=(
       select mgr from emp where ename='SMITH'
);

--查询在CHICAGO工作的人员信息
select * from emp where deptno=(
       select deptno from dept where loc='CHICAGO'
);
  • 常见错误

ORA-01427:单行子查询返回多个行:子查询返回了多行结果,外部查询使用=获取值

ORA-00913:值过多:子查询返回了多个列,外部只有一个条件进行接受

4.2 多行单列子查询

是指返回多行数据的子查询语句,在WHERE使用多行子查询时,要用:

IN:匹配于子查询结果的任一个值即可。

ALL:必须要符合子查询查询结果的所有值,一般与><!=进行连用。

ANY:只要符合子查询结果的人一个值即可。

  • IN示例

    --查询与10号部门存在工种相同工作的人员的信息
    SELECT ename,job, sal, deptno FROM emp WHERE job IN(
    SELECT distinct job FROM emp WHERE deptno=10
    );

    --查询JONES的下属组长管理的人员信息
    select * from emp where mgr in (
    select empno from emp where mgr=(
    select empno from emp where ename='JONES'
    )
    );

  • ALL示例

    --查询工资高于30号部门所有人工资的员工信息
    --比较次数多,效率低
    select * from emp where sal > all(
    select distinct sal from emp where deptno = 30
    );
    --等价于:查询比30号部门的最高工资高的员工信息
    select * from emp where sal > (
    select max(sal) from emp where deptno = 30
    );

  • ANY示例

    --查询比30号部门最低工资高的
    select * from emp where sal > any(
    select distinct sal from emp where deptno = 30
    );

4.3 多列子查询

  • 单行使用"="

  • 多行使用"in"

    --查询"SMITH'所在部门,和"SMITH'工种相同的人的信息
    select * from emp where
    deptno=(select deptno from emp where ename='SMITH')
    and
    job=(select job from emp where ename='SMITH');

    --使用()将对应数量的列包括
    select * from emp
    where (deptno,job)= (select deptno,job from emp where ename='SMITH');

4.4 在FROM子句中使用子查询

当在FROM子句中使用子查询,该子查询会被作为视图对待,要给子查询指定别名。

视图实际上就是将查询结果放入一张虚拟表中,多用于多表联合查询。

就是一条查询sql语句,用于显示一个或多个表或其他视图中的相关数据。

复制代码
--子查询当成表使用
select * from (select * from emp where deptno =20);

---虚拟表中使用别名
select t1.dno,t1.eno,t1.ename from 
(select deptno dno,empno eno,ename,sal,comm from emp where deptno =20) t1;

--每个部门里高于部门平均工资的员工信息
select emp.deptno,ename,job,sal,dept.avgsal from emp,
       (select deptno,avg(sal) as avgsal from emp group by deptno) dept
       where emp.deptno= dept.deptno and sal>dept.avgsal;

4.5 在DDL语句中使用子查询

  • 用在create语句中用于对表或表结构进行热备份

1=1 : 表示绝对等

1=2 : 表示绝对不等

复制代码
--备份的同时备份数据 --不会备份约束
create table emp_backup as select * from emp;
create table emp_backup1 as select * from emp where deptno=20;

--只备份表结构
create table emp_backup2 as select * from emp where 1=2;

4.6 在DML语句中使用子查询

  • INSERT

    --将查询结果当做值插入表中
    insert into emp_backup2 select * from emp where sal >1000;
    --列的数据类型必须相同
    insert into emp_backup2(empno,ename,job,sal,comm)
    select empno,ename,job,sal,comm from emp where sal <1000;

  • UPDATE

    --将和'SMITH'工作相同的人工资全部改为'SMITH'的工资和奖金水平
    update emp_backup2 set (sal,comm)=
    (select sal,comm from emp_backup2 where ename='SMITH')
    where job=(select job from emp_backup2 where ename='SMITH');

  • DELETE

    --将查询到的结果,从表中删除
    delete from emp_backup2 where deptno=
    (select deptno from dept where dname='SALES');

4.7 分页

ORACLE中使用子查询和伪列实现分页功能。

1、rowid 分页(rowid高速分页)

2、rownum分页

3、分析函数分页

总体而言rowid 分页效率最高,分析函数分页效率最低,但是rownum用的最多。

真分页(物理分页):直接从数据库取出页面需要的记录数,不加载表格中的全部记录

逻辑分页(内存分页):从数据库去除所有的记录,在服务器的内存进行分页的操作

4.7.1 rownum、rowid的概念

rownum,rowid都叫伪列。

rownum是逻辑上的编号,且其值总是从1开始,每行的rounum不是固定的。简单的说: ROWNUM是对结果集加的一个伪列,即先查到结果集之后再加上去的一个列 (强调:先要有结果集),是对符合条件结果的序列号。

rowid是数据的物理编号

4.7.2 rownum分页

  1. 查询rownum,将需要的分页的结果集作为子查询使用

    --确定数据源,排序在此步完成
    select rownum rn, e.* from (select * from emp) e;

    --正确:<=10 包含第一条记录
    select rownum , e.* from (select * from emp ) e where rownum <=10;
    --错误:rownum要从1开始
    select rownum , e.* from (select * from emp) e where rownum >=6;

  2. 将上一步的结果集再次作为虚拟表使用,并在上一步中给rownum起一个别名

    ---将rownum固定为虚拟表的一个列
    select * from (
    select rownum rn, e.* from (
    select * from emp) e
    )where rn between 1 and 10;

  3. 优化版 先查询上限的部分

    select * from (
    select rownum rn, e.* from
    ( select * from emp) e where rownum <=10
    ) where rn >=6;

4.7.3 rowid分页

rowid高速分页 只能针对表中的记录

  1. 查询需要分页的结果集rowid

    select rowid rd from emp;

  2. 利用rownum分页,查询出需要的分页记录的rowid

    select * from(
    select rownum rn,rd from(select rowid rd from emp) where rownum<=10
    )where rn>=6;

  3. 从物理表根据rowid取出相应的记录

    select * from emp where rowid in(
    select rd from(
    select rownum rn,rd from(
    select rowid rd from emp
    ) where rownum<=10
    )where rn>=6
    );

4.8 12C版本以上的分页方式

复制代码
--查询结果集中第1-5条记录
select * from emp order by sal 
fetch first 5 rows only;

--取21-30条记录
SELECT * FROM emp ORDER BY sal
OFFSET 20 ROWS 
FETCH NEXT 10 ROWS ONLY;

5 合并查询

在实际应用中,为了合并多个select语句的结果,可以使用集合操作符号:union、union all、intersect、minus,多用于数据量比较大的数据局库,运行速度快。

5.1 UNION

该操作符用于取得两个结果集的并集,并自动去掉结果集中重复行,两条sql的查询数量和类型要相同。

复制代码
select ename, sal, job from emp where sal>2500
union
select ename, sal, job from emp where job='MANAGER';
--两种方法效果相同,但union方法效率会更高
select ename, sal, job from emp where sal>2500 or job='MANAGER';

5.2 UNION ALL

union相似,但它不会取消重复行,而且不会排序。

复制代码
select ename, sal, job from emp where sal>2500
union all
select ename, sal, job from emp where job='MANAGER';

5.3 INTERSECT

该操作符用于取得两个结果集的交集。

复制代码
select ename, sal, job from emp where sal>2500
intersect
select ename, sal, job from emp where job='MANAGER';
--两种方法效果相同,and方法效率会更高一些,但intersect可用于多表
select ename, sal, job from emp where sal>2500 and job='MANAGER';

5.4 MINUS

使用该操作符用于取得两个结果集的差集,他只会显示存在第一个集合中,而不存在第二个集合中的数据。

复制代码
select ename, sal, job from emp where sal>2500
minus
select ename, sal, job from emp where job='MANAGER';

5.5 集合操作符的限制

对于LOB、VARRAY、嵌套表类来说,集合操作符无效

对于LONG型,UNION ALL、INTERSECT和MINUS无效

如果选择列表包含了表达式,必须指定别名

6 行列转换

  • 创建表

    create table sales_data (
    product_id number,
    sale_date date,
    amount number
    );

  • 插入数据

    insert into sales_data (product_id, sale_date, amount) values (1, TO_DATE('2023-01-15', 'YYYY-MM-DD'), 150);
    insert into sales_data (product_id, sale_date, amount) values (1, TO_DATE('2023-02-20', 'YYYY-MM-DD'), 200);
    insert into sales_data (product_id, sale_date, amount) values (1, TO_DATE('2023-03-25', 'YYYY-MM-DD'), 250);
    insert into sales_data (product_id, sale_date, amount) values (2, TO_DATE('2023-01-16', 'YYYY-MM-DD'), 180);
    insert into sales_data (product_id, sale_date, amount) values (2, TO_DATE('2023-02-21', 'YYYY-MM-DD'), 220);
    insert into sales_data (product_id, sale_date, amount) values (2, TO_DATE('2023-03-26', 'YYYY-MM-DD'), 270);

6.1 使用case进行行列转换

ORACLE和MYSQL通用的方式

复制代码
SELECT product_id,
       SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 1 THEN amount END) AS "Jan",
       SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 2 THEN amount END) AS "Feb",
       SUM(CASE WHEN EXTRACT(MONTH FROM sale_date) = 3 THEN amount END) AS "Mar"
FROM sales_data
GROUP BY product_id 
ORDER BY product_id;

6.2 使用 PIVOT 进行列转列操作

ORCLE特有的方式是Oracle 11g之后引入的一种用于行列转换的函数。它可以将查询结果中的行数据转换为列数据,从而实现行列转换。

  • 语法:

    SELECT ...
    FROM ...
    PIVOT (aggregate_function(column_to_aggregate)
    FOR column_to_pivot
    IN (list_of_values))

aggregate_function是一个聚合函数,如SUM、MAX、MIN等

column_to_aggregate是要进行聚合的列

column_to_pivot是要进行行列转换的列

list_of_values是要转换为列的值的列表

复制代码
SELECT *
FROM (
    SELECT 
        product_id,
        EXTRACT(MONTH FROM sale_date) AS month,
        amount
    FROM 
        sales_data
)
PIVOT (
    SUM(amount)
    FOR month IN (1 AS Jan, 2 AS Feb, 3 AS Mar)
)
ORDER BY product_id;
相关推荐
SelectDB31 分钟前
森马服饰从 Elasticsearch 到阿里云 SelectDB 的架构演进之路
大数据·数据库·数据分析
寒士obj1 小时前
MySQL偏门但基础的面试题集锦
数据库·mysql
唐叔在学习1 小时前
9类主流数据库 - 帮你更好地进行数据库选型!
数据库·redis·mysql·mongodb·nosql·大数据存储
失因2 小时前
Linux 权限管理与 ACL 访问控制
linux·运维·服务器·数据库·centos
cookqq3 小时前
mongodb源代码分析创建db流程分析
数据库·sql·mongodb·nosql
yh云想3 小时前
存储函数与触发器:数据库自动化与业务逻辑封装的核心技术
数据库·sql
山茶花开时。3 小时前
[Oracle] TO_DATE()函数
数据库·oracle
the beard3 小时前
MySQL进阶:(第八篇)深入解析InnoDB存储架构
数据库·mysql
Monika Zhang3 小时前
Redis缓存详解及常见问题解决方案
数据库·redis·缓存