简体   繁体   中英

Oracle REGEXP_LIKE lookahead and lookbehind workaround

I am struggling to build a regexp_like statement that will work in Oracle. I need to compare a series of rows, each of which has a field that contains a comma-delimited set of numbers. (always numbers). I need to activate and inactivate accounts (daily) based on how these numbers change.

Here are several samples of the fields. (each row below is the result in the field)

9,18,31,32,50,725
17,20,31,32,50,725
17,18,20,22,50,725
9,18,31,49,50,500
49,22,9,25,222
18,18,31,9,50,725
49,31,9,25,292

I need to find any row that has one or more of the following numbers. 9, 20, 22, 50. (Other places in the code may need only 9 or only 20 and 22. But the principle holds. If the number is 500, it cannot be selected. It must be 50.

The following regular expression works in python, in my notepad++ search, and in an online regex builder:

((?<![1-9])9|(?<![1-9])20(?![1-9])|(?<![1-9])22(?![1-9])|(?<![1-9])50(?![1-9]))

It relies on negative lookahead and negative lookbehind. But because Oracle doesn't support lookaround, I'm stumped.

Here is a piece of sample code which works if I use regexp without lookaround.

DECLARE 
v_search_string    varchar2(60);
BEGIN
v_search_string := '49,31,9,25,50,292';

IF REGEXP_LIKE( v_search_string, '((?<![1-9])9|(?<![1-9])20(?![1-9])|(?<![1-9])22(?![1-9])|(?<![1-9])50(?![1-9]))')
    THEN
        dbms_output.put_line('Yes');
    ELSE
        dbms_output.put_line('No');
END IF;
--dbms_output.put_line('Yes');
END;

In this case, I should get 'Yes' in my dbms_output because it should match '50'.

Any thoughts? PS I'm open to someone saying, "hey, silly, it would be easier to do THIS."

I wouldn't even use regex here:

DECLARE 
v_search_string    varchar2(60);
BEGIN
v_search_string := '49,31,9,25,50,292';

IF ',' || v_search_string || ',' LIKE '%,9,%' OR ',' || v_search_string || ',' LIKE '%,20,%' OR ',' || v_search_string || ',' LIKE '%,22,%' OR ',' || v_search_string || ',' LIKE '%,50,%'
    THEN
        dbms_output.put_line('Yes');
    ELSE
        dbms_output.put_line('No');
END IF;
--dbms_output.put_line('Yes');
END;

To be clear here, we are comparing ,49,31,9,25,50,292, against eg %,9,% to search for the number 9 somewhere in the CSV string. This approach may actually outperform REGEXP_LIKE as it does not use regular expressions.

...
where regexp_like(your_string, '(,|^)(9|20|22|50)(,|$)')
...

Generally I agree with Tim Biegeleisen - use standard string functions as much as possible.

In this case, a single regexp search may do better than four standard string searches of the same string; and the regexp approach is more flexible. You can rewrite the regular expression as

'(,|^)(' || things_to_search || ')(,|$)'

and let things_to_search be a user input, or come from a table, etc. (it would be a pipe-delimited list of numbers you must match). If you use it as a bind variable, you get the benefit of soft parsing (the query doesn't need to be parsed again whenever you change the list).

@mathguy. Thanks for taking my complex statement to a single, clear statement.

Starts with either a comma or start of line; ends with either a comma or end of line; plus the stuff in the middle. Change the variables for the things to search and let 'r rip.

Here's the modified sql.

DECLARE 
v_search_string    varchar2(60);
v_things_to_search    varchar2(60);
BEGIN
v_search_string := '9,31,29,25,520,500';
v_things_to_search := '9|20|22|50';

IF REGEXP_LIKE( v_search_string, '(,|^)(' || v_things_to_search || ')(,|$)')
    THEN
        dbms_output.put_line('Yes');
    ELSE
        dbms_output.put_line('No');
END IF;
--dbms_output.put_line('Yes');
END;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM