基于输入范围的排序逗号分隔字符串的 MAX/MIN 的 SQL 查询

Posted

技术标签:

【中文标题】基于输入范围的排序逗号分隔字符串的 MAX/MIN 的 SQL 查询【英文标题】:SQL query for MAX/MIN of a sorted comma separated string based on an input range 【发布时间】: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,应该对其进行计算。

【问题讨论】:

【参考方案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

【讨论】:

以上是关于基于输入范围的排序逗号分隔字符串的 MAX/MIN 的 SQL 查询的主要内容,如果未能解决你的问题,请参考以下文章

使用逗号分隔符的表格排序不起作用

使用 pandas 对列中以逗号分隔的字符串值进行排序

基于逗号分隔的字符串 -oracle 构建数组

C ++逗号分隔的输入数组代码过早退出

使用逗号分隔值重新排序DataTable行

ActionScript 3 AS3修剪逗号分隔字符串(例如,用户输入的逗号分隔关键字列表)