Postgres NÃO na matriz

96

Estou usando o tipo de array nativo do Postgres e tentando encontrar os registros onde o ID não está nos IDs de destinatário do array.

Posso descobrir onde eles estão em:

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

Mas isso não funciona:

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

Qual é a maneira certa de testar essa condição?

user577808
fonte
faz WHERE 3 NOT IN recipient_idso trabalho?
Janus Troelsen
1
Nota relacionada: como para text[]e int[]matriz:select not(array[1,2,3] @> array[3]);
Steve Peak
3
Dica profissional: se você estiver verificando se uma nullcoluna está contida ou não em um array, sempre dirá não. Levei cerca de 20 minutos depurando vários métodos de contenção para chegar à conclusão de que você não pode verificar se nulo está contido em um array
André Pena

Respostas:

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

Você sempre pode negar WHERE (condition)comWHERE NOT (condition)

Frank Farmer
fonte
2
@aschyiel - Você pode querer voltar para em ANYvez de INconforme sua recipient_idslista de entrada crescer: stackoverflow.com/questions/1009706/…
derekm
39

Você poderia inverter um pouco e dizer "3 não é igual a todos os IDs":

where 3 != all (recipient_ids)

Do bom manual :

9.21.4. ALL (array)

expression operator ALL (array expression)

O lado direito é uma expressão entre parênteses, que deve produzir um valor de matriz. A expressão do lado esquerdo é avaliada e comparada a cada elemento da matriz usando o operador fornecido , que deve produzir um resultado booleano. O resultado de ALLé "verdadeiro" se todas as comparações forem verdadeiras (incluindo o caso em que a matriz tem zero elementos). O resultado é "falso" se algum resultado falso for encontrado.

mu é muito curto
fonte
isso não explica realmente por anyque não funciona neste caso
seanlinsley
Isso deve ser aceito, pois explica o motivo corretamente. PS você também pode encontrar anye allno documento do postgres, que diz: " x <> ANY (a,b,c) é equivalente a x <> a OR <> b OR x <> c". ref: postgresqltutorial.com/postgresql-any postgresqltutorial.com/postgresql-all
Tyler Temp
19

Aumentando as ALL/ANYrespostas

Prefiro todas as soluções que usam allou anyque alcancem o resultado, valorizando as notas adicionais (por exemplo, sobre NULL s). Como outro acréscimo, aqui está uma maneira de pensar sobre esses operadores.

Você pode pensar neles como operadores de curto-circuito :

  • all(array)passa por todos os valores na matriz, comparando cada um com o valor de referência usando o operador fornecido. Assim que uma comparação resulta false, o processo termina com falso, caso contrário, verdadeiro. (Comparável ao lógico de curto-circuito and.)
  • any(array)passa por todos os valores na matriz, comparando cada um com o valor de referência usando o operador fornecido. Assim que uma comparação resulta true, o processo termina com verdadeiro, caso contrário, falso. (Comparável ao lógico de curto-circuito or.)

É por isso 3 <> any('{1,2,3}')que não produz o resultado desejado: o processo compara 3 com 1 para a desigualdade, o que é verdadeiro, e retorna imediatamente verdadeiro. Um único valor na matriz diferente de 3 é suficiente para tornar toda a condição verdadeira. O 3 na última posição da matriz é prob. nunca usado.

3 <> all('{1,2,3}')por outro lado, garante que todos os valores não sejam iguais a 3. Ele percorrerá todas as comparações que resultam em verdadeiro até um elemento que produz falso (o último neste caso), para retornar falso como o resultado geral. É isso que o OP deseja.

ThomasH
fonte
12

not (3 = any(recipient_ids))?

Markus Mikkolainen
fonte
Obrigado, eu estava usando 3 <> ANY(ARRAY[1,2,3,4]). Deveria ter funcionado assim: \
yeyo
11

uma atualização:

a partir do postgres 9.3,

você pode usar NOTem conjunto com o @> operador (contém) para fazer isso também.

IE.

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

Galo
fonte
11

Cuidado com os NULLs

Ambos ALL:

(some_value != ALL(some_array))

E ANY:

NOT (some_value = ANY(some_array))

Funcionaria, desde que some_arraynão seja nulo. Se a matriz pode ser nula, você deve contabilizá-la com coalesce (), por exemplo

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

Ou

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

Dos documentos :

Se a expressão da matriz gerar uma matriz nula, o resultado de ANY será nulo

Se a expressão da matriz resultar em uma matriz nula, o resultado de ALL será nulo

isapir
fonte
3

Observe que os operadores ANY / ALL não funcionarão com índices de matriz. Se os índices estão em mente:

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

e o negativo:

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

Um índice pode então ser criado como:

CREATE INDEX recipient_ids_idx on tableName USING GIN(recipient_ids)
Jamming James
fonte
Ao contrário de outras respostas, esta resposta realmente usa o operador de sobreposição de matriz do PostgreSQL. &&
Ceiling Gecko
6
Isso não funcionará como está escrito. Operadores de array como && e @> requerem que ambos os elementos sejam arrays, o que 3 não é. Para que isso funcione, a consulta terá de ser escrita como: SELECT COUNT(*) FROM "messages" WHERE ARRAY[3] && recipient_ids.
Dologan