Oracle REGEXP_LIKE 字符串中子字符串的逻辑和匹配
Oracle REGEXP_LIKE logical and matching of substrings in string
我有一个包含 'code1 code2 code3' 等代码的字符串。如果输入的所有代码都包含在字符串中,它应该 return 字符串。
例如:
select * from (
select 'avs cde jkl' code from dual)
where REGEXP_LIKE(code, 'REGEX-MAGIC')
当正则表达式现在类似于 ^(?=.*\bjkl\b)(?=.*\bavs\b).*$
时,它应该 return 代码。但是这种语法不适用于 oracle 中的正则表达式。
逻辑是'if all codes looked for are in the string (order does not matter), then return the code.'
我已经研究过,这可以通过积极的前瞻来实现,但据我所知,oracle 不支持这一点。我会搜索一个正则表达式,而不是像 REGEXP_LIKE(...,..) and REGEXP_LIKE(...,..) and ...
.
这样的结构
Oracle 版本为 12c。
如有任何帮助,我们将不胜感激!
我不擅长 regex-magix,但是 - 看看这样的事情是否有帮助。
这是一个包含这些代码的 table:
SQL> select * from codes;
ID CODE
---------- -----------
1 avs cde jkl
2 xyz avs
查询
- 将每个代码拆分成行(
t_split
CTE)
- 对输入的参数 (par_string) 值 (
p_split
CTE) 做同样的事情
- 为什么?这样它们就可以像 table 中的行一样工作,您可以应用
MINUS
set operator
- if
MINUS
returns nothing, 匹配;否则就是不匹配
SQL> with
2 -- split code to rows
3 t_split as
4 (select id,
5 code original_code,
6 regexp_substr(code, '[^ ]+', 1, column_value) code
7 from codes cross join
8 table(cast(multiset(select level from dual
9 connect by level <= regexp_count(code, ' ') + 1
10 ) as sys.odcinumberlist))
11 where id = &&par_id
12 ),
13 -- split parameter to rows
14 p_split as
15 (select regexp_substr('&&par_string', '[^ ]+', 1, level) code
16 from dual
17 connect by level <= regexp_count('&&par_string', ' ') + 1
18 )
19 --
20 -- if all parameter's "pieces" of code are contained in CODE value, MINUS returns nothing
21 -- so there's a match
22 select distinct t.original_code,
23 '&&par_string' par_string,
24 case when (select count(*)
25 from (select code from t_split
26 minus
27 select code from p_split
28 )
29 ) = 0 then 'Match'
30 else 'Mismatch'
31 end result
32 from t_split t
33 where t.id = &&par_id;
Enter value for par_id: 1
Enter value for par_string: jkl avs cde
ORIGINAL_CO PAR_STRING RESULT
----------- ----------- --------
avs cde jkl jkl avs cde Match
SQL> undefine par_string
SQL> /
Enter value for par_string: avs jkl www
ORIGINAL_CO PAR_STRING RESULT
----------- ----------- --------
avs cde jkl avs jkl www Mismatch
SQL>
根据您使用的工具(这是 SQL*Plus),您可能需要将 &&
替换为冒号 :
;或者,将这样一段代码转换为函数。
Oracle 不支持 look-ahead、look-behind 或正则表达式中的单词边界。
如果您有示例数据:
CREATE TABLE table_name (code) AS
SELECT 'avs cde jkl' FROM DUAL UNION ALL
SELECT 'avs cde' FROM DUAL UNION ALL
SELECT 'jkl avs' FROM DUAL UNION ALL
SELECT 'cde jkl' FROM DUAL;
选项 1:
最简单的查询是不使用正则表达式并使用多个 LIKE
条件查找 sub-string 匹配项:
SELECT code
FROM table_name
WHERE ' ' || code || ' ' LIKE '% avs %'
AND ' ' || code || ' ' LIKE '% jkl %'
输出:
CODE
avs cde jkl
jkl avs
选项 2:
您可以在多个 REGEXP_LIKE
条件下使用(较慢的)正则表达式:
SELECT code
FROM table_name
WHERE REGEXP_LIKE(code, '(^| )avs( |$)')
AND REGEXP_LIKE(code, '(^| )jkl( |$)')
输出与上面相同。
选项 3:
您可以将匹配放入 sub-query 分解子句,然后使用 LATERAL
连接:
WITH match_conditions (match) AS (
SELECT 'avs' FROM DUAL UNION ALL
SELECT 'jkl' FROM DUAL
)
SELECT code
FROM table_name t
CROSS JOIN LATERAL (
SELECT 1
FROM match_conditions
WHERE ' ' || code || ' ' LIKE '% ' || match || ' %'
HAVING COUNT(*) = (SELECT COUNT(*) FROM match_conditions)
)
输出与上面相同。
选项 4:
如果您真的想要一个正则表达式,那么您可以生成要匹配的代码的每个排列并将它们连接成一个正则表达式:
SELECT code
FROM table_name
WHERE REGEXP_LIKE(
code,
'(^| )avs( | .*? )jkl( |$)' -- Permutation 1
|| '|(^| )jkl( | .*? )avs( |$)' -- Permutation 2
)
输出与上面相同。
但是,随着要匹配的代码数量的增加,维护起来会出现问题,因为 2 个项目有 2 个排列,但 5 个项目有 5 个排列! = 120 个排列。
选项 5:
您可以声明一个嵌套的 table 集合:
CREATE TYPE string_list AS TABLE OF VARCHAR2(20);
然后拆分字符串(同样,您不需要慢速正则表达式)然后将其与嵌套的 table:
进行比较
WITH bounds (rid, code, spos, epos) AS (
SELECT ROWID, code, 1, INSTR(code, ' ', 1)
FROM table_name
UNION ALL
SELECT rid, code, epos + 1, INSTR(code, ' ', epos + 1)
FROM bounds
WHERE epos > 0
)
SEARCH DEPTH FIRST BY code SET order_rn
SELECT MAX(code) AS code
FROM bounds
GROUP BY rid
HAVING string_list('avs', 'jkl') SUBMULTISET OF CAST(
COLLECT(
CAST(
CASE epos
WHEN 0
THEN SUBSTR(code, spos)
ELSE SUBSTR(code, spos, epos - spos)
END
AS VARCHAR2(20)
)
)
AS string_list
);
根据您使用的客户端应用程序,您可以将整个 string_list('avs', 'jkl')
集合作为单个绑定变量传入,您可以从数组中填充该变量。 Java(以及一些建立在 Java 之上的语言)使用 ODBC 驱动程序可以做到这一点; C# 不能直接传递,但您可以传递关联数组并将其转换为带有辅助函数的嵌套 table 集合。
输出与上面相同。
db<>fiddle here
我有一个包含 'code1 code2 code3' 等代码的字符串。如果输入的所有代码都包含在字符串中,它应该 return 字符串。
例如:
select * from (
select 'avs cde jkl' code from dual)
where REGEXP_LIKE(code, 'REGEX-MAGIC')
当正则表达式现在类似于 ^(?=.*\bjkl\b)(?=.*\bavs\b).*$
时,它应该 return 代码。但是这种语法不适用于 oracle 中的正则表达式。
逻辑是'if all codes looked for are in the string (order does not matter), then return the code.'
我已经研究过,这可以通过积极的前瞻来实现,但据我所知,oracle 不支持这一点。我会搜索一个正则表达式,而不是像 REGEXP_LIKE(...,..) and REGEXP_LIKE(...,..) and ...
.
Oracle 版本为 12c。
如有任何帮助,我们将不胜感激!
我不擅长 regex-magix,但是 - 看看这样的事情是否有帮助。
这是一个包含这些代码的 table:
SQL> select * from codes;
ID CODE
---------- -----------
1 avs cde jkl
2 xyz avs
查询
- 将每个代码拆分成行(
t_split
CTE) - 对输入的参数 (par_string) 值 (
p_split
CTE) 做同样的事情 - 为什么?这样它们就可以像 table 中的行一样工作,您可以应用
MINUS
set operator - if
MINUS
returns nothing, 匹配;否则就是不匹配
SQL> with
2 -- split code to rows
3 t_split as
4 (select id,
5 code original_code,
6 regexp_substr(code, '[^ ]+', 1, column_value) code
7 from codes cross join
8 table(cast(multiset(select level from dual
9 connect by level <= regexp_count(code, ' ') + 1
10 ) as sys.odcinumberlist))
11 where id = &&par_id
12 ),
13 -- split parameter to rows
14 p_split as
15 (select regexp_substr('&&par_string', '[^ ]+', 1, level) code
16 from dual
17 connect by level <= regexp_count('&&par_string', ' ') + 1
18 )
19 --
20 -- if all parameter's "pieces" of code are contained in CODE value, MINUS returns nothing
21 -- so there's a match
22 select distinct t.original_code,
23 '&&par_string' par_string,
24 case when (select count(*)
25 from (select code from t_split
26 minus
27 select code from p_split
28 )
29 ) = 0 then 'Match'
30 else 'Mismatch'
31 end result
32 from t_split t
33 where t.id = &&par_id;
Enter value for par_id: 1
Enter value for par_string: jkl avs cde
ORIGINAL_CO PAR_STRING RESULT
----------- ----------- --------
avs cde jkl jkl avs cde Match
SQL> undefine par_string
SQL> /
Enter value for par_string: avs jkl www
ORIGINAL_CO PAR_STRING RESULT
----------- ----------- --------
avs cde jkl avs jkl www Mismatch
SQL>
根据您使用的工具(这是 SQL*Plus),您可能需要将 &&
替换为冒号 :
;或者,将这样一段代码转换为函数。
Oracle 不支持 look-ahead、look-behind 或正则表达式中的单词边界。
如果您有示例数据:
CREATE TABLE table_name (code) AS
SELECT 'avs cde jkl' FROM DUAL UNION ALL
SELECT 'avs cde' FROM DUAL UNION ALL
SELECT 'jkl avs' FROM DUAL UNION ALL
SELECT 'cde jkl' FROM DUAL;
选项 1:
最简单的查询是不使用正则表达式并使用多个 LIKE
条件查找 sub-string 匹配项:
SELECT code
FROM table_name
WHERE ' ' || code || ' ' LIKE '% avs %'
AND ' ' || code || ' ' LIKE '% jkl %'
输出:
CODE avs cde jkl jkl avs
选项 2:
您可以在多个 REGEXP_LIKE
条件下使用(较慢的)正则表达式:
SELECT code
FROM table_name
WHERE REGEXP_LIKE(code, '(^| )avs( |$)')
AND REGEXP_LIKE(code, '(^| )jkl( |$)')
输出与上面相同。
选项 3:
您可以将匹配放入 sub-query 分解子句,然后使用 LATERAL
连接:
WITH match_conditions (match) AS (
SELECT 'avs' FROM DUAL UNION ALL
SELECT 'jkl' FROM DUAL
)
SELECT code
FROM table_name t
CROSS JOIN LATERAL (
SELECT 1
FROM match_conditions
WHERE ' ' || code || ' ' LIKE '% ' || match || ' %'
HAVING COUNT(*) = (SELECT COUNT(*) FROM match_conditions)
)
输出与上面相同。
选项 4:
如果您真的想要一个正则表达式,那么您可以生成要匹配的代码的每个排列并将它们连接成一个正则表达式:
SELECT code
FROM table_name
WHERE REGEXP_LIKE(
code,
'(^| )avs( | .*? )jkl( |$)' -- Permutation 1
|| '|(^| )jkl( | .*? )avs( |$)' -- Permutation 2
)
输出与上面相同。
但是,随着要匹配的代码数量的增加,维护起来会出现问题,因为 2 个项目有 2 个排列,但 5 个项目有 5 个排列! = 120 个排列。
选项 5:
您可以声明一个嵌套的 table 集合:
CREATE TYPE string_list AS TABLE OF VARCHAR2(20);
然后拆分字符串(同样,您不需要慢速正则表达式)然后将其与嵌套的 table:
进行比较WITH bounds (rid, code, spos, epos) AS (
SELECT ROWID, code, 1, INSTR(code, ' ', 1)
FROM table_name
UNION ALL
SELECT rid, code, epos + 1, INSTR(code, ' ', epos + 1)
FROM bounds
WHERE epos > 0
)
SEARCH DEPTH FIRST BY code SET order_rn
SELECT MAX(code) AS code
FROM bounds
GROUP BY rid
HAVING string_list('avs', 'jkl') SUBMULTISET OF CAST(
COLLECT(
CAST(
CASE epos
WHEN 0
THEN SUBSTR(code, spos)
ELSE SUBSTR(code, spos, epos - spos)
END
AS VARCHAR2(20)
)
)
AS string_list
);
根据您使用的客户端应用程序,您可以将整个 string_list('avs', 'jkl')
集合作为单个绑定变量传入,您可以从数组中填充该变量。 Java(以及一些建立在 Java 之上的语言)使用 ODBC 驱动程序可以做到这一点; C# 不能直接传递,但您可以传递关联数组并将其转换为带有辅助函数的嵌套 table 集合。
输出与上面相同。
db<>fiddle here