Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Postgresql Text Array get rows NOT LIKE certain value?

Tags:

sql

postgresql

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?

like image 648
Rotareti Avatar asked Jun 21 '26 12:06

Rotareti


2 Answers

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

like image 91
sticky bit Avatar answered Jun 24 '26 01:06

sticky bit


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);
like image 25
Gordon Linoff Avatar answered Jun 24 '26 03:06

Gordon Linoff



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!