【问题标题】:Use format pattern from regexp_replace in to_char在 to_char 中使用来自 regexp_replace 的格式模式
【发布时间】:2018-05-14 11:04:43
【问题描述】:

我有一个 SQL 存储在其中日期格式在方括号中的列中:

sql_column
------------------------------------------------------------------------------
select col1 from table1 where col2 = to_date('[DD-MON-YYYY]', 'DD-MON-YYYY');
select col1 from table2 where col2 between [YYYYMMDD]00000 and [YYYYMMDD]99999 and col3 = to_date('[DD-MON-YYYY]', 'DD-MON-YYYY');
....

我不知道事先可以有哪种格式,但它始终是日期格式。

有没有办法使用 regexp_replace(或 regexp_substr 甚至与 regexp_* 函数)来查找模式并将其替换为我给定日期的 to_char 的结果和从 db 列中获取的模式。

可能是这样的(显然不起作用):

select sql_column, 
    regexp_replace(sql_column, '\[(.+?)\]', to_char(some_date, '\1'))
from my_table;

你能帮忙吗?

【问题讨论】:

  • 按照这个select sql_column, regexp_substr(sql_column, '\[(.+?)\]', 1, 1, NULL, 1) as new_format, regexp_replace(sql_column, '\[(.+?)\]', '[' || to_char(some_date, new_format) || ']') from my_table;结果如何?
  • @revo - 我试过regexp_replace(sql_column, '\[(.+?)\]', '[' || to_char(date'2016-06-01', '\1') || ']'),它不起作用 - 给出错误ORA-01821: date format not recognized
  • 您的错误是关于日期格式的尝试修复它。
  • 这个日期格式需要来自正则表达式的反向引用
  • 您无法使用 REGEXP_* 函数捕获格式模型并将其用于同一 REGEXP_* 函数中对 TO_DATE 的调用。您似乎还有未指定数量的日期需要更换;这意味着您将需要某种递归查询(可能是递归 CTE)来从模板和正确数量的输入(绑定变量或您传入的任何其他内容)构造正确的 SQL 语句。那么,显而易见的问题是:为什么 SQL 语句以这种方式存储,而不是应有的视图?

标签: sql regex oracle backreference


【解决方案1】:

无法解析所有可能的日期格式。例如,如果您在 varchar 字段中看到“01/11/2017”,则无法确定它是指 11 月 1 日还是 1 月 11 日。

也就是说,您可以使用通用表表达式来选择最佳模式,然后使用它将字符串值转换为日期。例如:

select * from
(select
    case 
        when REGEXP_LIKE(col2, '^[0-9]{2}/[0-9]{2}/[0-9]{4}$') then 'DD/MM/YYYY'
        when REGEXP_LIKE(col2, '^[0-9]{14}$') then 'YYYYMMDDHH24MISS'
    end pattern, table1.*
from table1) x
where to_date(x.col2, x.pattern) = to_date('01/11/2017','DD/MM/YYYY')

这种方法可能会导致全表扫描,因此远非高效。如果原始表很大,我建议使用转换后的日期创建一个物化视图以提高性能。

【讨论】:

    【解决方案2】:

    您需要一些动态 SQL 来解决这个问题。 我没有提供完整的解决方案,但这应该会给你一个提示。

    让我们自下而上。

    您真正需要的是一个 REPLACE 语句,它将您的 SQL 文本转换为所需形式的参数日期。

    这可能是您的示例第一个示例的此语句(参数日期为 30.11.2017)

     select replace(sql_column, '[DD-MON-YYYY]','30-NOV-2017') from my_table; 
    

    如果您在第一行运行第一条语句,您会得到预期的结果:

     select col1 from table1 where col2 = to_date('30-NOV-2017', 'DD-MON-YYYY');
    

    那么如何获得那些 REPLACE 语句。一种可能是编写一个 PL/SQL 函数。

    该函数有两个参数 - 原始 SQL 字符串和参数日期。 使用正则表达式可以废弃日期格式掩码。 使用动态 SQL (EXECUTE IMMEDIATE),您可以将参数 DATE 格式化为具有适当格式的字符串。 最后返回 REPLACE 语句。

    create or replace  function format_date(i_txt IN VARCHAR2, i_date DATE) return VARCHAR2 is
    v_date_format VARCHAR2(4000);
    v_form_date VARCHAR2(4000);
    v_param VARCHAR2(4000);
    v_form VARCHAR2(4000);
    v_sql VARCHAR2(4000);
    BEGIN
      v_param := regexp_substr(i_txt, '\[(.+?)\]');
      v_date_format := replace(replace(v_param,'[',null),']',null);
      v_sql := 'select to_char(:d,'''||v_date_format||''') as my_dt from dual';
      execute immediate v_sql into v_form_date using i_date;   
      v_form := 'select replace(sql_column, '''||v_param||''','''||v_form_date||''') from my_table';
      return (v_form);
    END;
    /
    

    请注意,我只处理字符串中的第一个日期掩码,您需要循环所有出现的位置才能正确处理第二个示例!

    【讨论】:

      猜你喜欢
      • 2019-11-25
      • 2018-05-31
      • 2023-03-27
      • 1970-01-01
      • 1970-01-01
      • 2010-11-01
      • 2014-10-22
      • 2016-01-27
      • 2021-10-04
      相关资源
      最近更新 更多