Oracle does not support look-ahead, look-behind or word boundaries in regular expressions.
If you have the sample data:
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;
Option 1:
The simplest query is to not use regular expressions and to look for sub-string matches using multiple LIKE
conditions:
SELECT code
FROM table_name
WHERE ' ' || code || ' ' LIKE '% avs %'
AND ' ' || code || ' ' LIKE '% jkl %'
Which outputs:
Option 2:
You could use (slower) regular expressions with multiple REGEXP_LIKE
conditions:
SELECT code
FROM table_name
WHERE REGEXP_LIKE(code, '(^| )avs( |$)')
AND REGEXP_LIKE(code, '(^| )jkl( |$)')
Which outputs the same as above.
Option 3:
You could put the matches into a sub-query factoring clause and then use a LATERAL
join:
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)
)
Which outputs the same as above.
Option 4:
If you really want a single regular expression then you can generate each permutation of the codes to match and concatenate them into a single regular expression:
SELECT code
FROM table_name
WHERE REGEXP_LIKE(
code,
'(^| )avs( | .*? )jkl( |$)' -- Permutation 1
|| '|(^| )jkl( | .*? )avs( |$)' -- Permutation 2
)
Which outputs the same as above.
However, this is going to get problematic to maintain as the number of codes to match grows as, for 2 items there are 2 permutations but for 5 items there are 5! = 120 permutations.
Option 5:
You could declare a nested table collection:
CREATE TYPE string_list AS TABLE OF VARCHAR2(20);
Then split the string (again, you do not need slow regular expressions) and then compare it to a nested 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
);
Depending on the client application you are using, you can pass the entire string_list('avs', 'jkl')
collection in as a single bind variable that you can populate from an array. Java (and some languages built on top of Java) using an ODBC driver can do this; C# cannot directly but you can pass an associative array and convert it to a nested table collection with a helper function.
Which outputs the same as above.
db<>fiddle here