Postgres НЕ в массиве

100

Я использую собственный тип массива Postgres и пытаюсь найти записи, идентификатор которых отсутствует в идентификаторах получателей массива.

Я могу найти, где они В:

SELECT COUNT(*) FROM messages WHERE (3 = ANY (recipient_ids))

Но это не работает:

SELECT COUNT(*) FROM messages WHERE (3 != ANY (recipient_ids))
SELECT COUNT(*) FROM messages WHERE (3  = NOT ANY (recipient_ids))

Как правильно проверить это состояние?

user577808
источник
делает WHERE 3 NOT IN recipient_idsработу?
Janus Troelsen
1
Связанное примечание: as for text[]and int[]array:select not(array[1,2,3] @> array[3]);
Steve Peak
3
Совет от профессионала: если вы проверяете, содержится ли nullстолбец в массиве или нет, он всегда скажет «нет». Мне потребовалось около 20 минут отладки нескольких содержащих методов, чтобы прийти к выводу, что вы не можете проверить, содержится ли null в массиве
Андре Пена

Ответы:

139
SELECT COUNT(*) FROM "messages" WHERE NOT (3 = ANY (recipient_ids))

Вы всегда можете свести на нет WHERE (condition)сWHERE NOT (condition)

Фрэнк Фармер
источник
2
@aschyiel - Возможно, вы захотите переключиться обратно, ANYа не по INмере recipient_idsроста вашего входного списка: stackoverflow.com/questions/1009706/…
derekm
41

Вы можете немного перевернуть его и сказать "3 не равно всем идентификаторам":

where 3 != all (recipient_ids)

Из прекрасного руководства :

9.21.4. ВСЕ (массив)

expression operator ALL (array expression)

Правая часть - это выражение в скобках, которое должно давать значение массива. Левое выражение оценивается и сравнивается с каждым элементом массива с помощью заданного оператора , который должен давать логический результат. Результатом ALLбудет «истина», если все сравнения вернут истину (включая случай, когда в массиве нет элементов). Результат будет «ложным», если будет обнаружен какой-либо ложный результат.

mu слишком короткий
источник
это не совсем объясняет, почему anyв данном случае не работает
seanlinsley 05
С этим следует согласиться, поскольку это правильно объясняет причину. PS вы также можете найти anyи allв postgres doc, где написано: « x <> ANY (a,b,c) эквивалентно x <> a OR <> b OR x <> c». Ссылка: postgresqltutorial.com/postgresql-any postgresqltutorial.com/postgresql-all
Тайлер Темп
19

Дополнение ALL/ANYответов

Я предпочитаю все решения, которые используют allили anyдля достижения результата, принимая во внимание дополнительные примечания (например, о NULL ). В качестве еще одного дополнения, вот способ подумать об этих операторах.

Вы можете думать о них как об операторах короткого замыкания :

  • all(array)перебирает все значения в массиве, сравнивая каждое с эталонным значением с помощью предоставленного оператора. Как только сравнение falseзавершается, процесс завершается ложью, в противном случае - истинным. (Сравнимо с логикой короткого замыкания and.)
  • any(array)перебирает все значения в массиве, сравнивая каждое с эталонным значением с помощью предоставленного оператора. Как только сравнение trueзавершается, процесс завершается истинным, в противном случае - ложным. (Сравнимо с логикой короткого замыкания or.)

Вот почему 3 <> any('{1,2,3}')не дает желаемого результата: процесс сравнивает 3 с 1 на предмет неравенства, что верно, и немедленно возвращает истину. Единственного значения в массиве, отличного от 3, достаточно, чтобы выполнить все условие. Число 3 в последней позиции массива - это вероятность. никогда не использовался.

3 <> all('{1,2,3}')с другой стороны, гарантирует, что все значения не равны 3. Он будет проходить через все сравнения, которые дают true, до элемента, который дает false (последний в этом случае), чтобы вернуть false в качестве общего результата. Это то, чего хочет ОП.

ThomasH
источник
12

not (3 = any(recipient_ids))?

Маркус Микколайнен
источник
Спасибо, пользовался 3 <> ANY(ARRAY[1,2,3,4]). Это должно было работать таким образом: \
ёё,
12

обновление:

начиная с postgres 9.3,

вы также можете использовать NOTв тандеме с @> оператором (contains), чтобы добиться этого.

IE.

SELECT COUNT(*) FROM "messages" WHERE NOT recipient_ids @> ARRAY[3];

Петух
источник
12

Остерегайтесь NULL

Оба ALL:

(some_value != ALL(some_array))

И ANY:

NOT (some_value = ANY(some_array))

Будет работать, пока some_arrayне равно нулю. Если массив может быть нулевым, вы должны учесть его с помощью coalesce (), например

(some_value != ALL(coalesce(some_array, array[]::int[])))

Или

NOT (some_value = ANY(coalesce(some_array, array[]::int[])))

Из документов :

Если выражение массива дает нулевой массив, результатом ANY будет NULL.

Если выражение массива дает нулевой массив, результатом ALL будет NULL

Isapir
источник
3

Обратите внимание, что операторы ANY / ALL не работают с индексами массива. Если имеются в виду индексы:

SELECT COUNT(*) FROM "messages" WHERE 3 && recipient_ids

и отрицательный:

SELECT COUNT(*) FROM "messages" WHERE NOT (3 && recipient_ids)

Затем можно создать индекс, например:

CREATE INDEX recipient_ids_idx on tableName USING GIN(recipient_ids)
глушить Джеймса
источник
В отличие от других ответов, в этом ответе фактически используется оператор перекрытия массива PostgreSQL. &&
Ceiling Gecko
6
Это не будет работать так, как написано. Для операторов массивов, таких как && и @>, оба элемента должны быть массивами, а 3 - нет. Для того , чтобы это работало, то запрос будет необходимо записать в виде: SELECT COUNT(*) FROM "messages" WHERE ARRAY[3] && recipient_ids.
Dologan