【问题标题】:SQL query for MAX/MIN of a sorted comma separated string based on an input range基于输入范围的排序逗号分隔字符串的 MAX/MIN 的 SQL 查询
【发布时间】:2020-12-20 12:33:25
【问题描述】:

我在 oracle 表中有一个列,其中包含按如下排序顺序以逗号分隔的日期值。

ID|LIST_OF_DATES
--|---------------
1 |2020-11-01,2020-11-04,2020-11-05,2020-11-10,2020-11-12,2020-11-14,2020-11-16,.....2020-11-26,2020-11-30

这里的日期列表不是连续的日期列表。我的要求是获取落在输入窗口内的日期列表的最大值的索引。上面提供的数据的示例输出。

INPUT_DATE_FROM: 2020-11-10
INPUT_DATE_TO: 2020-11-15

对于上述输入范围.. 我们有三个日期在该范围内,它们是 2020-11-10,2020-11-12,2020-11-14,其中 2020-11-14 是最大值。 2020-11-14 是日期列表中的第 6 个值,这是我需要的查询的输出。

OUTPUT:
ID|INDEX_OF_MAX_DATE
1 |5 (Assuming 0 is the start index)

当输入窗口更改为 2020-11-25 到 2020-11-30 时,我们只有两个日期,其中 2020-11-30 是最大值。我需要这个最大值的索引。

使用 oracle SQL 获得此结果的最佳方法是什么?我的表将有大约 4 -500 万个唯一 ID,应该对其进行计算。

【问题讨论】:

    标签: sql oracle window-functions


    【解决方案1】:

    您需要拆分列表,然后选择最大值。这是一种方法:

    with dates as (
          select regexp_substr(t.list_of_dates, '[^,]+', 1, level) as dte
          from  t
          connect by regexp_substr(t.list_of_dates, '[^,]+', 1, level) is not null
         )
    select id, max(date)
    from dates
    where dte >= :start_date_str and date <= :end_date_str
    group by id;
    

    也就是说,您应该修复您的数据模型。在一个字符串中存储多个值是不好的。将日期存储为字符串是不好的。

    上面假设参数是字符串,就像字符串中的值一样。如果它们是日期,那么您需要将字符串转换为日期。

    【讨论】:

    • 缺少日期转换,当前使用保留字作为派生列名。
    【解决方案2】:

    您可以使用简单的字符串函数拆分字符串,然后使用解析函数查找列表中最大值的索引:

    WITH bounds ( id, list_of_dates, idx, start_pos, end_pos ) AS (
      SELECT id,
             list_of_dates,
             0,
             1,
             INSTR( list_of_dates, ',', 1 )
      FROM   table_name
    UNION ALL
      SELECT id,
             list_of_dates,
             idx + 1,
             end_pos + 1,
             INSTR( list_of_dates, ',', end_pos + 1 )
      FROM   bounds
      WHERE  end_pos > 0
    ),
    dates ( id, idx, value ) AS (
      SELECT id,
             idx,
             TO_DATE( 
               CASE end_pos
               WHEN 0
               THEN SUBSTR( list_of_dates, start_pos )
               ELSE SUBSTR( list_of_dates, start_pos, end_pos - start_pos )
               END,
               'YYYY-MM-DD'
             )
      FROM   bounds
    )
    SELECT id,
           idx,
           value
    FROM   (
      SELECT id,
             idx,
             value,
             RANK() OVER ( PARTITION BY id ORDER BY value DESC ) AS rnk
      FROM   dates
      WHERE  value BETWEEN DATE '2020-11-10' AND DATE '2020-11-15'
    )
    WHERE  rnk = 1;
    

    其中,对于样本数据:

    CREATE TABLE table_name ( ID, LIST_OF_DATES ) AS
    SELECT 1, '2020-11-01,2020-11-04,2020-11-05,2020-11-10,2020-11-12,2020-11-14,2020-11-16,2020-11-26,2020-11-30' FROM DUAL
    

    输出:

    身份证 | IDX |价值 -: | --: | :----------------- 1 | 5 | 2020-11-14 00:00:00

    db小提琴here


    或者通过使用:

    SELECT t.id,
           d.*
    FROM   table_name t
           CROSS APPLY (
             SELECT *
             FROM   (
               SELECT LEVEL - 1 AS idx,
                      TO_DATE(
                        REGEXP_SUBSTR( t.list_of_dates, '[^,]+', 1, LEVEL ),
                        'YYYY-MM-DD'
                      ) AS value
               FROM   DUAL
               CONNECT BY
                      LEVEL <= REGEXP_COUNT( t.list_of_dates, '[^,]+' )
             )
             WHERE  value BETWEEN DATE '2020-11-10' AND DATE '2020-11-15'
             ORDER BY
                    value DESC
             FETCH FIRST ROW WITH TIES
           ) d
    

    db小提琴here

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-10-25
      • 1970-01-01
      • 2019-08-07
      • 1970-01-01
      • 2017-09-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多