标签:inter rownum 行号 通过 round 实现 lan 最大值 roman
? 简介
在前面的 Oracle 查询 SELECT 语句(一) 中介绍了 SELECT 常用的一些基本查询语法,接下来再来看 SELECT 更深入的一些查询功能和技巧,包括以下内容:
1. All 与 Any 运算符
2. 分页查询(rownum)
3. 集合操作符(UNION、UNION ALL、INTERSECT、MINUS)
1. All 与 Any 运算符
1) All 运算符,表示满足给出列表中的所有值。通常用于以下场景:
1. 查出大于30号部门所有员工最高工资的员工姓名、工资
--使用ALL
SELECT ename, sal FROM emp WHERE sal > ALL(SELECT sal FROM emp WHERE deptno = 30);
--使用MAX
SELECT ename, sal FROM emp WHERE sal > (SELECT MAX(sal) FROM emp WHERE deptno = 30);
2) Any 运算符,表示满足给出列表中的任意值。通常用于以下场景:
1. 查出大于30号部门任意员工的工资的员工姓名、工资
--使用ALL
SELECT ename, sal FROM emp WHERE sal > ANY(SELECT sal FROM emp WHERE deptno = 30);
--使用MIN
SELECT ename, sal FROM emp WHERE sal > (SELECT MIN(sal) FROM emp WHERE deptno = 30);
n 提示
通过以上示例,可以看到通常情况下,ALL 和 ANY 都可以使用 MAX 和 MIN 去取代,所有这两个运算符一般情况用的不多。分析如下:
比较运算符 | All | Any | Max | Min |
> 大于 | 取最大值 | 取最小值 | 取最大值 | 取最小值 |
< 小于 | 取最小值 | 取最小值 | 取最大值 | 取最小值 |
2. 分页查询(rownum)
在 Oracle 中分页,我们通常需要借助 ROWNUM 这个伪列来进行实现。这个列并不是真实存在的,当我们进行每一个 SELECT 查询时,Oracle 都会帮我们自动产生的这个序列号(rownum),用于标识行号。下面来看具体实现,比如我们需要取 emp 表中4到6行的记录:
1) 首先,我们来看一个奇怪的现象
SELECT * FROM emp WHERE rownum >= 4 AND rownum <= 6;
啪,一执行,呀,怎么没数据啊?这并不是我们写错了,要解释这个问题,我们先来看一个图,就明白其中原由了。
由图可以看出,当我们取出第一条记录时,此时(rownum = 1) >= 3不成立,所以该记录会被排除;然后再取第二条,此时任然 rownum = 1,因为只有成功满足一条记录,rownum 才会加1,所以不满足又被排除掉了。这样依次类推,最终都不满足条件,所以全部都被排除掉了。所以,以下语句始终查不出数据:
SELECT * FROM emp WHERE rownum > 1;
然后,在看另外一边(就是接下来用的这种判断方式),首先取第一条(满足),第二条也满足,直到(rownum = 7) <= 6,所以会取出6条记录,此时 rownum 的值为1,2,3,4,5,6。好了,搞清楚原理后我们就来实现。
2) 根据对 rownum 的分析,便改为以下语句【推荐】
SELECT rownum, t1.* FROM (
SELECT rownum rnum, t1.* FROM emp t1 WHERE rownum <= 6
) t1 WHERE t1.rnum >= 4;
这样,通过子查询,先取出前6行,再过滤掉前3行,就得到了我们需要的数据。注意:之前提过,每个 SELECT 都会产生一个 rownum 序列号,所有上面会可以输出两个 rownum 序列号,dual 也不例外:
SELECT t1.*, rownum FROM dual t1;
3) 除了使用以上语句,我们还可以这样写
SELECT rownum, t1.* FROM (
SELECT rownum rnum, t1.* FROM emp t1
) t1 WHERE t1.rnum >= 4 AND rnum <= 6; --或使用 BETWEEN 子句
同样,可以完成以上功能。但分析一下,这种方式视乎没有上面的方式效率高,因为,这里是先查出所有(先将 rownum 分配好)数据,再进行第二次 rownum 过滤。
4) 有时候,我们还需要通过排序后再分页,该怎么实现呢?
使用排序并分页,也需要注意以下问题。
首先,我们来看下排序的全部数据:
SELECT * FROM emp ORDER BY sal;
按照上面的要求,我们应该是取出 empno(7521,7654,7934) 的员工,OK。
不是说用第一种方式,效率很高么?那就来使用它实现,更改的后的 SQL:
SELECT * FROM (
SELECT rownum rnum, t1.* FROM emp t1 WHERE rownum <= 6 ORDER BY sal
) WHERE rnum >= 4;
结果是不是又纳闷了?怎么7698和7566也出来,而且还不是按我们预想的排序的!
好,我们再做个假设,以上语法是不是先查询出结果后,再将结果集过滤和排序的呢?为了验证这个疑点,很简单我们做以下测试:
SELECT * FROM (
SELECT * FROM (SELECT rownum rnum, t1.* FROM emp t1)
WHERE rownum <= 6 ORDER BY sal
) WHERE rnum >= 4;
结果与前面的推断是一样的,就是先查询出结果(产生的 rownum 是没有经过排序的),再排序,最后分页(过滤)。我们看一下未排序的原始数据:
SELECT rownum, t1.* FROM emp t1;
所以,我们得出一个结论:当我们同时过滤 rownum 和排序时,是先按默认的排序生成 rownum 后,再进行排序和过滤的。
5) 其实上面的排序和分页,并不是准确有效的。因为我们需要的是,rownum 的顺序是根据我们指定的排序产生的,这样再进行分页才是准确的。所以正确的排序和分页应该这样写:
SELECT * FROM (
SELECT rownum rnum, t1.* FROM (SELECT * FROM emp ORDER BY sal) t1
) WHERE rnum BETWEEN 4 AND 6;
执行步骤:先根据指定的字段排序;再产生 rownum 序列号;最后进行分页。
3. 集合操作符(UNION、UNION ALL、INTERSECT、MINUS)
在 Oracle 中对多个集合操作,主要使用 UNION、UNION ALL、INTERSECT、MINUS 这四个操作符来完成,解决取多个集合的并集、交集和差集。
1) UNION、UNION ALL
UNION 和 UNION ALL 用于取两个结果集的并集,举例说明:
? UNION,取出并集并去除重复行,例如:
SELECT * FROM emp WHERE empno <= 7500
UNION
SELECT * FROM emp WHERE empno < 7600;
说明:
1. 两个查询的结果集中,都有两条记录小于7500,UNION 在合并时进行去重了,所以相同的记录只会显示一行。
2. UNION 逻辑上可以合并任意个结果集,所以只需要在后面接着写 UNION 即可。
? UNION ALL,取出并集不会去除重复行,例如:
SELECT * FROM emp WHERE empno <= 7500
UNION ALL
SELECT * FROM emp WHERE empno < 7600;
可见,两个结果集中具有两条相同的记录,UNION ALL 并没有去重。
2) INTERSECT
INTERSECT 用于取两个结果集中相交的记录,即取得交集记录。还是以刚才的数据为例:
SELECT * FROM emp WHERE empno <= 7500
INTERSECT
SELECT * FROM emp WHERE empno < 7600;
在两个查询的结果集中,存在两个完全相同的记录,这就是交集。
3) MINUS
MINUS 中文意思是减去,表示第一个结果集减去第二个结果集,取出它们的差集。任然以刚才的数据为例:
SELECT * FROM emp WHERE empno <= 7600
MINUS
SELECT * FROM emp WHERE empno < 7500;
取得两条记录,这两条记录是在第一个结果集中存在,在第二个结果集中不存在的记录,则为差集。
标签:inter rownum 行号 通过 round 实现 lan 最大值 roman
原文地址:https://www.cnblogs.com/abeam/p/12115330.html