2

Scenario: Match any string that starts with "J01" except the string "J01FA09".

I'm baffled why the following code returns nothing:

SELECT 1
WHERE
    '^J01(?!FA09).*' ~ 'J01FA10'

when I can see on regexr.com that it's working (I realize there are different flavors of regex and that could be the reason for the site working).

I have confirmed in the postgres documentation that negative look aheads are supported though.

Table 9-15. Regular Expression Constraints

(?!re) negative lookahead matches at any point where no substring matching re begins (AREs only). Lookahead constraints cannot contain back references (see Section 9.7.3.3), and all parentheses within them are considered non-capturing.

4
  • Just to be sure do you have any rows in your table? Because it does seem you wrote SELECT 1 WHERE TRUE, which should work as long as there are any rows Commented May 9, 2017 at 17:02
  • @Daniel, please check my answer. Commented May 9, 2017 at 19:14
  • 1
    Right side operand...so obvious. I realize I don't need regex for my exact example but this is part of a much bigger picture. Commented May 9, 2017 at 20:34
  • @Xyzk There doesn't need to be a FROM clause or a table for this very simple example to work . It was simply used to test the regex Commented May 9, 2017 at 20:40

2 Answers 2

4

Match any string that starts with "J01" except the string "J01FA09".

You can do without a regex using

WHERE s LIKE 'J01%' AND s != 'J01FA09'

Here, LIKE 'J01%' requires a string to start with J01 and then may have any chars after, and s != 'J01FA09' will filter out the matches.

If you want to ahieve the same with a regex, use

WHERE s ~ '^J01(?!FA09$)'

The ^ matches the start of a string, J01 matches the literal J01 substring and (?!FA09$) asserts that right after J01 there is no FA09 followed with the end of string position. IF the FA09 appears and there is end of string after it, no match will be returned.

See the online demo:

CREATE TABLE table1
    (s character varying)
;

INSERT INTO table1
    (s)
VALUES
    ('J01NNN'),
    ('J01FFF'),
    ('J01FA09'),
    ('J02FA09')
;
SELECT * FROM table1 WHERE s ~ '^J01(?!FA09$)';

SELECT * FROM table1 WHERE s LIKE 'J01%' AND s != 'J01FA09';

enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

Marking as the answer since you've added the end of string piece '$' that I hadn't seen yet. My biggest problem was just that my silly test case had the operand sides swapped.
@Daniel: Yes, another improvement is that you actually do not need .* at the end as the regex check performed with ~ does not require a full string match (unlike LIKE or SIMILAR TO patterns that need a whole string match).
3

RE is a right side operand:

SELECT 1
WHERE 'J01FA10' ~ '^J01(?!FA09)';

 ?column? 
----------
        1
(1 row) 

4 Comments

However, with this pattern, OP won't achieve the required results.
@WiktorStribiżew - I do not know how OP is going to use it, but this works well.
Really appreciate you pointing out the obvious flaw in my operand sides. That's what was mostly stumping me on getting this simple look ahead working before I put it into the more complex code.
I think the original question was only missing the end anchor ($) from the negative lookahead.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.