2

我正在处理一些存储在 VARCHAR2(4000) 列中的数据,这些数据主要是文本注释,但也包含文本中的日期。我编写了一个查询,它使用 SUBSTR 和 INSTR 进行模式匹配并找到日期之前的前导文本值,然后 SUBST 返回日期值,然后我使用 TO_DATE 将其转换为日期。这很好用,但是我有许多包含多个评论和多个日期的记录。使用上述方法,我只能指定此模式的第 (n) 次出现,有没有办法在匹配时返回所有日期,而不仅仅是一次出现?

这是 varchar 列中的数据示例;

LOCKED ENTITY: ACCOUNT
LOCKED BY USER: ops
LOCKED AT: 31/05/2004 11:47
CUST NOTES: <Please enter explanation here>
Customer notes are entered here.

UNLOCKED ENTITY: ACCOUNT
UNLOCKED BY USER: ops
UNLOCKED AT: 31/05/2004 11:49
UNLOCK NOTES: <Please enter explanation here>
Test

LOCKED ENTITY: USER
LOCKED BY USER: ops
LOCKED AT: 31/05/2004 11:50
LOCK NOTES: <Please enter explanation here>
Test

UNLOCKED ENTITY: USER
UNLOCKED BY USER: ops
UNLOCKED AT: 24/08/2009 16:47
UNLOCKED NOTES: <Please enter explanation here>

这是我正在使用的查询的简化版本(为清楚起见,删除了所有其他不相关的连接和列);

select substr(VALUE, INSTR(VALUE,'LOCKED AT: ',1)+11, 10) as "DATE"
  from tableA a
  join tableB b
    on a.id = b.id
 where regexp_like (VALUE ,'ABC|DEF|GHI');  

DATE
----------
31/05/2004

对于上述查询,我​​想返回字符串 'LOCKED AT:' 之后的所有日期,例如;

 DATE
----------
31/05/2004
31/05/2004

任何帮助,将不胜感激。有关数据库版本是 Oracle 10g 的信息,我尝试使用 REGEXP_COUNT 来实现我的想法,但仅适用于 11g 以后。

非常感谢

4

3 回答 3

3

您可以使用regexp_substrwithconnect by level来获取所有匹配项:

提供样本数据:

WITH tableA(VALUE) AS (SELECT 'LOCKED ENTITY: ACCOUNT
LOCKED BY USER: ops
LOCKED AT: 31/05/2004 11:47
CUST NOTES: <Please enter explanation here>
Customer notes are entered here.

UNLOCKED ENTITY: ACCOUNT
UNLOCKED BY USER: ops
UNLOCKED AT: 31/05/2004 11:49
UNLOCK NOTES: <Please enter explanation here>
Test

LOCKED ENTITY: USER
LOCKED BY USER: ops
LOCKED AT: 31/05/2004 11:50
LOCK NOTES: <Please enter explanation here>
Test

UNLOCKED ENTITY: USER
UNLOCKED BY USER: ops
UNLOCKED AT: 24/08/2009 16:47
UNLOCKED NOTES: <Please enter explanation here>' FROM dual)

询问:

select ltrim( regexp_substr(VALUE,
                            '^LOCKED AT: ([[:digit:]]{2}/[[:digit:]]{2}/[[:digit:]]{4}\.?)',1,LEVEL,'m'), 
                            'LOCKED AT: ') as "Dates Locked" 
  FROM tableA
 CONNECT BY regexp_substr(VALUE,
                            '^LOCKED AT: ([[:digit:]]{2}/[[:digit:]]{2}/[[:digit:]]{4}\.?)',1,LEVEL,'m') IS NOT null

结果:

Dates Locked
------------
31/05/2004
31/05/2004

这个查询的问题是,它connect by是为整个表执行的——为了避免这种可能的解决方案是分离这部分并为任何数据行单独执行它:

SELECT ID, ltrim( regexp_substr(VALUE,
                            '^LOCKED AT: ([[:digit:]]{2}/[[:digit:]]{2}/[[:digit:]]{4}\.?)',1,lvl.column_value,'m'), 
                            'LOCKED AT: ') as "Dates Locked" 
  FROM tableA
  CROSS JOIN table(cast(multiset
    (select level from dual
    connect by regexp_substr(VALUE,
                            '^LOCKED AT: ([[:digit:]]{2}/[[:digit:]]{2}/[[:digit:]]{4}\.?)',1,LEVEL,'m') IS NOT null) 
    as sys.odcinumberlist)) lvl
于 2019-10-25T10:58:49.600 回答
2

您可以将regexp_substr()regexp_count()和正则表达式函数与字符串运算符函数一起regexp_like()使用:regexp_instr()substr()

with tab as
(
select 
       regexp_substr(value,
              'LOCKED AT: ([[:digit:]]{2}/[[:digit:]]{2}/[[:digit:]]{4}\.?)',1,level)
              as Dates_Locked,
       regexp_instr(value,
              'LOCKED AT: ([[:digit:]]{2}/[[:digit:]]{2}/[[:digit:]]{4}\.?)',1,level)      
              as Pos_Unlocked,
       value 
  from tableA a 
  join tableB b 
    on a.id = b.id
connect by level <= regexp_count(value,'LOCKED AT: ') 
)
select ltrim( Dates_Locked, 'LOCKED AT: ' ) as Dates_Locked
  from tab     
 where not regexp_like(substr(value,Pos_Unlocked-2,2),'UN','i');

 Dates Locked
 ------------
 31/05/2004
 31/05/2004

Demo

于 2019-10-25T10:50:19.540 回答
0

您可以在没有正则表达式的情况下完成所有操作,只需使用简单的字符串函数:

甲骨文设置

CREATE TABLE test_data ( value ) AS
SELECT 'LOCKED ENTITY: ACCOUNT
LOCKED BY USER: ops
LOCKED AT: 31/05/2004 11:47
CUST NOTES: <Please enter explanation here>
Customer notes are entered here.

UNLOCKED ENTITY: ACCOUNT
UNLOCKED BY USER: ops
UNLOCKED AT: 31/05/2004 11:49
UNLOCK NOTES: <Please enter explanation here>
Test

LOCKED ENTITY: USER
LOCKED BY USER: ops
LOCKED AT: 31/05/2004 11:50
LOCK NOTES: <Please enter explanation here>
Test

UNLOCKED ENTITY: USER
UNLOCKED BY USER: ops
UNLOCKED AT: 24/08/2009 16:47
UNLOCKED NOTES: <Please enter explanation here>' FROM DUAL;

查询

首先通过双回车(使用递归子查询)溢出字符串,然后您可以找到:和回车分隔符的索引并使用它们来提取数据。

WITH note_bounds ( value, start_pos, end_pos ) AS (
  SELECT REPLACE( value, CHR(13) ),
         1,
         INSTR( REPLACE( value, CHR(13) ), CHR(10) || CHR(10), 1 )
  FROM   test_data
UNION ALL
  SELECT value,
         end_pos + 2,
         INSTR( value, CHR(10) || CHR(10), end_pos + 2 )
  FROM   note_bounds
  WHERE  end_pos > 0
),
notes ( note ) AS (
  SELECT CASE end_pos
         WHEN 0
         THEN SUBSTR( value, start_pos )
         ELSE SUBSTR( value, start_pos, end_pos - start_pos )
         END
  FROM   note_bounds
),
note_data_bounds ( note, sep1, end1, sep2, end2, sep3, end3, sep4 ) AS (
  SELECT note,
         INSTR( note, ':', 1, 1 ),
         INSTR( note, CHR(10), 1, 1 ),
         INSTR( note, ':', 1, 2 ),
         INSTR( note, CHR(10), 1, 2 ),
         INSTR( note, ':', 1, 3 ),
         INSTR( note, CHR(10), 1, 3 ),
         INSTR( note, ':', 1, 5 )
  FROM   notes
)
SELECT SUBSTR( note, 1, sep1 - 8 ) AS action,
       SUBSTR( note, sep1 + 2, end1 - sep1 - 2 ) AS entity,
       SUBSTR( note, sep2 + 2, end2 - sep2 - 2 ) AS actor,
       TO_DATE( SUBSTR( note, sep3 + 2, end3 - sep3 - 2 ), 'DD/MM/YYYY HH24:MI' ) AS datetime,
       SUBSTR( note, sep4 + 2 ) AS notes
FROM   note_data_bounds;

输出

行动 | 实体 | 演员 | 日期时间 | 笔记                                                              
:------- | :-------- | :---- | :----------------- | :------------------------------------------------ -----------------
锁定 | 帐户 | 运维 | 2004-05-31 11:47:00 | <请在此处输入说明><br>在此处输入客户备注。
解锁 | 帐户 | 运维 | 2004-05-31 11:49:00 | <请在此处输入说明><br>测试                            
锁定 | 用户 | 运维 | 2004-05-31 11:50:00 | <请在此处输入说明><br>测试                            
解锁 | 用户 | 运维 | 2009-08-24 16:47:00 | <请在此处输入说明>                                    

db<>在这里摆弄

于 2019-10-25T13:16:31.420 回答