3

Let's say I have a simple table such as this one:

id_ | tags
---------------
0   | foo1, baz
1   | bar1, qux

The id_ column is of type SERIAL and the tags column is of type TEXT[] (text array).

In order to search the tags column with the LIKE operator I use a combination of unnest and DISTINCT ON like this:

SELECT DISTINCT ON (id_) *
FROM (
  SELECT unnest(tags) tag, *
  FROM Records
) x
WHERE
  (tag LIKE '%o%');

This works fine. The query returns row 0 like it should.

Now I'm trying to figure out a way to invert the query, so that it only returns rows that don't match the LIKE expression. I tried with this:

WHERE
  (tag NOT LIKE '%o%');

but it doesn't seem to work... My idea was that this query should return row 1 only, but it returns both rows.

I also tried with sub queries, for example like this one:

    WHERE
      (x.id_ NOT IN (SELECT id_ FROM Records WHERE tag like '%o%'));

But it still returns both rows.

Does anyone know how this can be fixed?

2 Answers 2

2

You could use ALL to check, that all unnested tags are NOT LIKE '%o%' by using Postgres' ability to return Booleans for comparison expressions.

SELECT DISTINCT ON (id_) *
       FROM records
       WHERE true = ALL (SELECT tag NOT LIKE '%o%'
                                FROM unnest(tags) tag);

If one tag is LIKE '%o', NOT LIKE '%o%' will return false for that tag and no longer are all selected vales true what would be necessary for true = ALL (...) to be true.

BTW, you could also easily negate this using ANY and LIKE instead:

SELECT DISTINCT ON (id_) *
       FROM records
       WHERE true = ANY (SELECT tag LIKE '%o%'
                                FROM unnest(tags) tag);

(or possibly other combinations of true or false, = ALL or = ANY, (SELECT tag LIKE ...) or (SELECT tag NOT LIKE ...))

SQL Fiddle

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

2 Comments

You don't need the true = part. PG is good about Booleans, unlike SQL Server and Oracle.
@jpmc26: I do need it, as the ALL or ANY operator needs something to compare to. Just WHERE ALL (...) throws a syntax error.
2

like only has to match one tag. not like has to match all of them. Hence, aggregation:

SELECT id_
FROM (SELECT unnest(tags) tag, *
      FROM Records
     ) x
GROUP BY id_
HAVING SUM( (tag LIKE '%o%')::int) = 0;

Or use bool_and():

HAVING bool_and( (tag LIKE '%o%')::int);

2 Comments

Why convert to int and use SUM instead of the dedicated bool_and and bool_or aggregate functions?
@jpmc26 . . . Good point. It is a habit because I use a lot of different databases.

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.