需要帮助了解 SQL 窗口函数之间的范围

Posted

技术标签:

【中文标题】需要帮助了解 SQL 窗口函数之间的范围【英文标题】:Need help understanding range between in SQL window functions 【发布时间】:2018-11-21 15:25:06 【问题描述】:

我正在尝试了解 range 子句在以下情况下的工作原理(oracle 数据库)

SELECT
    EMPID,NAME, 
    HIRE_DATE_1, 
    SALARY,
    count(1) over(order by HIRE_DATE_1 range between 1 preceding and 1 preceding) as PREV_MIN_SA
FROM (
    SELECT 
        EMPID,
        NAME,
        (EXTRACT(year from HIRE_DATE)*10000)+(EXTRACT(MONTH FROM HIRE_DATE) * 100) + (extract(DAY from HIRE_DATE)) as HIRE_DATE_1,SALARY
     FROM EMPLOYEE A order by HIRE_DATE,SALARY
) A
ORDER BY HIRE_DATE_1 

结果集:

EMPID   NAME    HIRE_DATE_1 SALARY  PREV_MIN_SA
100     Ravi    20180101    5000    0
101     Kumar   20180101    7000    0
102     Satish  20180101    13000   0
103     Naresh  20180102    7500    3
105     Lalith  20180104    17300   0
104     Suresh  20180104    40000   0
106     Latha   20180201    16000   0

内部查询只是将日期转换为数字 YYYYMMDD 格式。 我的目的是计算每条记录中员工日期之前加入的人数。我可以计算具有相同 HIRE_DATE 的行数并使用 LAG 函数,但不知何故不明白 sql 如何返回此结果集。

此外,一旦我完成了计数,我想获得在当前行中的员工之前加入的员工的 MIN(SALARY),并找到薪水的差异,所以想知道我是否可以以某种方式定义窗口仅包含紧接在 HIRE_DATE 之前的所有记录。

谢谢

【问题讨论】:

您使用的是哪个 dbms? 请解释你要做什么。 计算HIRE_DATE_1 正好比当前行的HIRE_DATE_1 少一的行数。因为您努力将日期转换为整数,所以它将“打破”月份边界和年份边界。你为什么要把数据转换成整数!? 我使用的是 Oracle Live SQL(在线)所以不确定它在后台运行的是什么版本 @MatBailie :我在想“前面有 1 的范围”是指有序集中的值,而不是正好是 1 减。我转换为整数,因为我更喜欢整数算术而不是处理时间戳(oracle 似乎捕获日期数据类型中的时间戳) 【参考方案1】:

这应该得到之前的雇用日期...

SELECT
    EMPID,NAME, HIRE_DATE, SALARY,
    MAX(HIRE_DATE) OVER (ORDER BY HIRE_DATE
                            RANGE BETWEEN UNBOUNDED PRECEDING
                                      AND INTERVAL '1 DAY' PRECEDING
                        )
                           AS PREV_HIRE_DATE
FROM
    EMPLOYEE

那么我认为您需要重新加入员工表以查找员工人数和他们的最低工资?

SELECT
    *
FROM
(
    SELECT
        EMPID,NAME, HIRE_DATE, SALARY,
        MAX(HIRE_DATE) OVER (ORDER BY HIRE_DATE
                                RANGE BETWEEN UNBOUNDED PRECEDING
                                          AND INTERVAL '1 DAY' PRECEDING
                            )
                               AS PREV_HIRE_DATE
    FROM
        EMPLOYEE
)
    EMPS
LEFT JOIN
(
    SELECT
        HIRE_DATE,
        COUNT(*)     AS COUNT_EMPS,
        MIN(SALARY)  AS MIN_SALARY
    FROM
        EMPLOYEE
    GROUP BY
        HIRE_DATE
)
    PREV_EMPS
        ON PREV_EMPS.HIRE_DATE = EMPS.PREV_HIRE_DATE

编辑:

也许可以尝试这样的事情? (我必须跑,祝你好运!)

WITH
  ranked AS
(
    SELECT
      *,
      DENSE_RANK() OVER (ORDER BY HIRE_DATE)   AS HIRE_SEQ_ID
    FROM
      EMPLOYEE
)
SELECT
  *,
  MIN(SALARY) OVER (ORDER BY HIRE_SEQ_ID
                       RANGE BETWEEN 1 PRECEDING AND 1 PRECEDING
                   )
                      AS PREV_MIN_SALARY,
  COUNT(*)    OVER (ORDER BY HIRE_SEQ_ID
                       RANGE BETWEEN 1 PRECEDING AND 1 PRECEDING
                   )
                      AS COUNT_PREV_EMPS
FROM
  ranked

【讨论】:

@zulfi123786 运气好吗?【参考方案2】:

我相信这就是你所追求的(我把它作为练习留给你找出最高和最低工资之间的差异!):

WITH employee AS (SELECT 100 empid, 'Ravi' NAME, to_date('01/01/2018', 'dd/mm/yyyy') hire_date, 5000 salary FROM dual UNION ALL
                  SELECT 101 empid, 'Kumar' NAME, to_date('01/01/2018', 'dd/mm/yyyy') hire_date, 7000 salary FROM dual UNION ALL
                  SELECT 102 empid, 'Satish' NAME, to_date('01/01/2018', 'dd/mm/yyyy') hire_date, 13000 salary FROM dual UNION ALL
                  SELECT 103 empid, 'Naresh' NAME, to_date('02/01/2018', 'dd/mm/yyyy') hire_date, 7500 salary FROM dual UNION ALL
                  SELECT 104 empid, 'Lalith' NAME, to_date('04/01/2018', 'dd/mm/yyyy') hire_date, 17300 salary FROM dual UNION ALL
                  SELECT 105 empid, 'Suresh' NAME, to_date('04/01/2018', 'dd/mm/yyyy') hire_date, 40000 salary FROM dual UNION ALL
                  SELECT 106 empid, 'Latha' NAME, to_date('01/02/2018', 'dd/mm/yyyy') hire_date, 16000 salary FROM dual)
SELECT empid,
       NAME,
       hire_date,
       salary,
       COUNT(*) OVER (ORDER BY hire_date RANGE BETWEEN 1 PRECEDING AND 1 PRECEDING) prev_day_hire_count,
       MIN(salary) OVER (ORDER BY hire_date RANGE BETWEEN 1 PRECEDING AND 1 PRECEDING) prev_day_min_sal,
       MAX(salary) OVER (ORDER BY hire_date RANGE BETWEEN 1 PRECEDING AND 1 PRECEDING) prev_day_max_sal
FROM   employee;

     EMPID NAME   HIRE_DATE       SALARY PREV_DAY_HIRE_COUNT PREV_DAY_MIN_SAL PREV_DAY_MAX_SAL
---------- ------ ----------- ---------- ------------------- ---------------- ----------------
       100 Ravi   01/01/2018        5000                   0                  
       101 Kumar  01/01/2018        7000                   0                  
       102 Satish 01/01/2018       13000                   0                  
       103 Naresh 02/01/2018        7500                   3             5000            13000
       104 Lalith 04/01/2018       17300                   0                  
       105 Suresh 04/01/2018       40000                   0                  
       106 Latha  01/02/2018       16000                   0                                

【讨论】:

EMPID 为 104 的行应该得到 PREV_DAY_MIN_SAL=7500。对于此记录,之前的日期是 '02/01/2018',并且只有一条记录,因此请考虑 7500。

以上是关于需要帮助了解 SQL 窗口函数之间的范围的主要内容,如果未能解决你的问题,请参考以下文章

窗口函数 - 日期之间的范围

带有条件的 Spark SQL 窗口函数范围边界

使用窗口函数根据时间搜索行

最大日期间隔的分析函数范围窗口

SQL Server 中的窗口函数(2012 新函数)

SQL Server 2012 Windowing 函数计算运行总计