如果无法对数据进行规范化,则可以选择动态地将 CSV 值转换为多行。您还可以使用正则表达式模式,例如:
select EVENT_ID, REPORT_DATE, RFD
from DB
where regexp_like(lower(RFD), '(^| |,)' || to_char(REPORT_DATE, 'fmday') || '(,| |$)')
or regexp_like(lower(RFD), '(^| |,)' || to_char(REPORT_DATE, 'fmddth') || '(,| |$)')
or (regexp_like(lower(RFD), '(^| |,)last(,| |$)') and REPORT_DATE = last_day(REPORT_DATE));
EVENT_ID | REPORT_DATE | RFD
-------: | :---------- | :--------
1 | 2021-02-01 | 1st,15th
2 | 2021-02-16 | Tuesday
4 | 2021-02-01 | 1st
5 | 2021-02-28 | 1st, Last
db<>fiddle
但是,如果您要处理大量数据,性能可能是个问题。
如果要将 CSV 字符串拆分为行,有多种方法,包括再次使用正则表达式:
select EVENT_ID, REPORT_DATE, regexp_substr(RFD, '(.*?)(,|$)', 1, level, NULL, 1) as RFD
from DB
connect by EVENT_ID = prior EVENT_ID
and level <= regexp_count(RFD, ',') + 1
and prior dbms_random.value is not null
或使用递归 CTE 和更简单/更快的字符串函数:
with rcte (EVENT_ID, REPORT_DATE, VALUE, RFD) AS (
select EVENT_ID, REPORT_DATE,
lower(trim(case when instr(RFD, ',') > 0 then substr(RFD, 1, instr(RFD, ',') - 1) else RFD end)),
lower(trim(case when instr(RFD, ',') > 0 then substr(RFD, instr(RFD, ',') + 1) end))
from DB
where RFD is not null
union all
select EVENT_ID, REPORT_DATE,
trim(case when instr(RFD, ',') > 0 then substr(RFD, 1, instr(RFD, ',') - 1) else RFD end),
trim(case when instr(RFD, ',') > 0 then substr(RFD, instr(RFD, ',') + 1) end)
from rcte
where RFD is not null
)
...
然后您可以检查各个行:
...
select EVENT_ID, REPORT_DATE, VALUE
from rcte
where VALUE = to_char(REPORT_DATE, 'fmday')
or VALUE = to_char(REPORT_DATE, 'fmddth')
or (VALUE = 'last' and REPORT_DATE = last_day(REPORT_DATE))
db<>fiddle
使用这种方法,您可能会获得多次点击 - 例如,如果您的第二个 ID 具有 RFD Tuesday, 16th - 您可能想要或可能需要抑制。
与往常一样,在速度和内存使用之间存在权衡,因此探索多种解决方案可能是个好主意。