代码之家  ›  专栏  ›  技术社区  ›  1pluszara

Oracle:拆分分隔字符串并选择大于输入日期的日期

  •  1
  • 1pluszara  · 技术社区  · 7 年前

    我在下面的列中有一个逗号半文字字符串作为值。

    '2015/04/01 11 GG, 2015/08/03 78 KK, 2012/12/12 44 TT, 2015/09/01 77 YY, 2015/09/01 88 ZZ'
    

    其中,每个字符串有三列,由空格组合和分隔。

    所以这里的要求是选择更大和更接近给定输入日期的日期。 得到第二列。

    例子: 如果输入日期为 01-OG-2015 那么我的输出应该是 七十八 因为它更近。 如果没有日期大于输入日期,则输出应为空。

    2 回复  |  直到 7 年前
        1
  •  3
  •   GMB    7 年前

    这是一个复杂的需求,正如Gordon Linoff所评论的那样,如果数据已经正确地分布,那么解决这个需求就简单多了。

    以下是一种方法:

    • 首先使用递归CTE REGEXP_SUBSTR CONNECT BY 使用逗号分隔符将字符串拆分为行
    • 然后将每行拆分为3列,再次使用 正则表达式 和空间分隔符
    • 然后使用Oracle窗口函数 DENSE_RANK KEEP 隔离相关行

    假设数据来自列 str 在表中 my_table :

    WITH 
        cte0 AS (
            SELECT TRIM(REGEXP_SUBSTR(str, '[^,]+', 1, LEVEL)) str
            FROM my_table
            CONNECT BY INSTR(str, ',', 1, LEVEL - 1) > 0
        ),
        cte1 AS (
            SELECT 
                TO_DATE(REGEXP_SUBSTR(str, '\S+', 1, 1), 'yyyy-mm-dd') dt,
                REGEXP_SUBSTR(str, '\S+', 1, 2) val1,
                REGEXP_SUBSTR(str, '\S+', 1, 3) val2
            FROM cte0
            ORDER BY 1 DESC
        )
    SELECT 
        MIN(dt)   keep (dense_rank first order by dt) as dt,
        MIN(val1) keep (dense_rank first order by dt) as val1,
        MIN(val2) keep (dense_rank first order by dt) as val2
    FROM cte1
    WHERE dt > TO_DATE(?, 'yyyy-mm-dd')
    

    …哪里 ? 是输入日期。

    * db<>fiddle here

     with 
         data as  (
             SELECT
                 '2015/04/01 11 GG, 2015/08/03 78 KK, 2012/12/12 44 TT, 2015/09/01 77 YY, 2015/09/01 88 ZZ' str
             FROM DUAL
         ),
         cte0 AS (
             SELECT TRIM(REGEXP_SUBSTR(str, '[^,]+', 1, LEVEL)) str
             FROM data
             CONNECT BY INSTR(str, ',', 1, LEVEL - 1) > 0
         ),
         cte1 AS (
             SELECT 
                 TO_DATE(REGEXP_SUBSTR(str, '\S+', 1, 1), 'yyyy-mm-dd') dt,
                 REGEXP_SUBSTR(str, '\S+', 1, 2) val1,
                 REGEXP_SUBSTR(str, '\S+', 1, 3) val2
             FROM cte0
             ORDER BY 1 DESC
         )
     SELECT 
         min(dt) keep (dense_rank first order by dt) as dt,
         min(val1) keep (dense_rank first order by dt) as val1,
         min(val2) keep (dense_rank first order by dt) as val2
     FROM cte1
     WHERE dt > TO_DATE('2015-08-01', 'yyyy-mm-dd')
    
    
    -------------------------
     DT        | VAL1 | VAL2
     :-------- | :--- | :---
     03-AUG-15 | 78   | KK  
    
        2
  •  1
  •   Littlefoot    7 年前

    这里有一个选项,基于您提供的示例数据。

    SQL> with test (col) as
      2    (select '2015/04/01 11 GG, 2015/08/03 78 KK, 2012/12/12 44 TT, 2015/09/01 77 YY, 2015/09/01 88 ZZ' from dual),
      3  t_comma as
      4    (select trim(regexp_substr(col, '[^,]+', 1, level)) col2
      5     from test
      6     connect by level <= regexp_count(col, ',') + 1
      7    ),
      8  t_diff as
      9    (select col2,
     10         substr(col2, 1, 10) c_date,
     11         regexp_substr(col2, '\d+', 1, 4) c_num,
     12         regexp_substr(col2, '\w+$') c_let ,
     13         --
     14         abs(to_date(substr(col2, 1, 10), 'yyyy/mm/dd') -
     15             to_date('&&:par_date', 'yyyy/mm/dd')) diff_days,
     16         --
     17         row_number() over (order by abs(to_date(substr(col2, 1, 10), 'yyyy/mm/dd') -
     18                                         to_date('&&par_date', 'yyyy/mm/dd'))) rn
     19     from t_comma
     20    )
     21  select c_num
     22  from t_diff
     23  where rn = 1;
    Enter value for par_date: 2015-08-01
    
    C_NUM
    --------------------------------------------------------------------------------
    78
    
    SQL>
    

    它是做什么的?

    • TEST 是你的样品 桌子 (由CTE代表)
    • T_COMMA 将逗号分隔值字符串拆分为行(这样您可以从示例数据中获得5行)
    • T_DIFF 提取子字符串的每个部分(即每行),计算采样日期和参数化日期之间的差异,并按差异的绝对值对其进行排序。- RN = 1 是“最近”的日期吗?
    • 决赛 SELECT 只返回“最近”的值