SQL KEEP 窗口函数等价改写案例

一哥们出条sql题给我玩,将下面sql改成不使用keep分析函数的写法。

复制代码
select deptno,
       ename,
       sal,
       hiredate,
       min(sal) keep(dense_rank first order by hiredate) over(partition by deptno) min_sal,
       max(sal) keep(dense_rank last order by hiredate) over(partition by deptno) max_sal
  from emp;

我一开始改错了,被这哥们喷菜鸡,我草。

复制代码
-- 错误等价改写,逻辑不等价
with x as (
select e1.deptno,
       e1.ename,
       e1.sal,
       e1.hiredate,
       row_number() over (partition by DEPTNO order by HIREDATE) rn_first,
       row_number() over (partition by DEPTNO order by HIREDATE DESC) rn_last
from EMP e1)
select
    e.deptno,
    e.ename,
    e.sal,
    e.hiredate,
    x1.SAL,
    x2.SAL
from emp e
    inner join x x1 on e.DEPTNO = x1.DEPTNO and x1.rn_first = 1
    inner join x x2 on e.DEPTNO = x2.DEPTNO and x2.rn_last = 1;

我换了张表测试下,发现上面改写是逻辑有问题,如果同一个组内有相同日期的分组字段内有NULL值的,确实会导致SQL结果集不一致。

复制代码
-- 将EMP表替换成EMPLOYEES,如果使用上面等价改写就错误了。
select DEPARTMENT_ID,
       FIRST_NAME,
       SALARY,
       HIRE_DATE,
       min(SALARY) keep(dense_rank first order by HIRE_DATE) over(partition by DEPARTMENT_ID) min_sal,
       max(SALARY) keep(dense_rank last order by HIRE_DATE) over(partition by DEPARTMENT_ID) max_sal
from EMPLOYEES;

最终等价改写的SQL,增加了分组字段内有NULL值的逻辑和处理一个组内有相同日期的逻辑。

复制代码
select e.DEPARTMENT_ID,
       e.FIRST_NAME,
       e.SALARY,
       e.HIRE_DATE,
       (select MIN_SALARY
        from (select DEPARTMENT_ID, MIN(SALARY) MIN_SALARY
              from (select DEPARTMENT_ID,
                           SALARY,
                           HIRE_DATE,
                           dense_rank() over (PARTITION BY DEPARTMENT_ID ORDER BY HIRE_DATE) RN
                    from EMPLOYEES)
              WHERE RN = 1
              GROUP BY DEPARTMENT_ID) e1
        where case when e1.DEPARTMENT_ID is null then 99999 else e1.DEPARTMENT_ID end = case when e.DEPARTMENT_ID is null then 99999 else e.DEPARTMENT_ID end) a_min,
       (select MAX_SALARY
        from (select DEPARTMENT_ID, MAX(SALARY) MAX_SALARY
              from (select DEPARTMENT_ID,
                           SALARY,
                           HIRE_DATE,
                           dense_rank() over (PARTITION BY DEPARTMENT_ID ORDER BY HIRE_DATE DESC) RN
                    from EMPLOYEES)
              WHERE RN = 1
              GROUP BY DEPARTMENT_ID) e1
        where case when e1.DEPARTMENT_ID is null then 99999 else e1.DEPARTMENT_ID end = case when e.DEPARTMENT_ID is null then 99999 else e.DEPARTMENT_ID end ) a_max
FROM EMPLOYEES e;

差集比较后是等价的:

相关推荐
武子康1 小时前
Java-06 深入浅出 MyBatis - 一对一模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据仓库·sql·mybatis·springboot·springcloud
爱上口袋的天空2 小时前
09 - Clickhouse的SQL操作
数据库·sql·clickhouse
Yang.994 小时前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3
王ASC8 小时前
ORA-01461: 仅能绑定要插入 LONG 列的 LONG 值。ojdbc8版本23.2.0.0驱动BUG【已解决】
数据库·sql·oracle
执键行天涯9 小时前
【日常经验】修改大数据量的表字段类型,怎么修改更快
sql
sevevty-seven10 小时前
幻读是什么?用什么隔离级别可以防止幻读
大数据·sql
天涯倦客的美丽人生15 小时前
SQL递归查询树结构语法
数据库·sql
OceanBase数据库官方博客17 小时前
OceanBase 中常用的查询语句
sql·oceanbase·分布式数据库·查询语句
网络安全指导员17 小时前
SQL注入的那些面试题总结
数据库·sql·安全·web安全·系统安全
奈斯ing17 小时前
【Oracle篇】SQL性能优化实战案例(从15秒优化到0.08秒)(第七篇,总共七篇)
运维·数据库·sql·oracle·性能优化