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?


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:



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.


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.


12

not (3 = any(recipient_ids))?


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];


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


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)

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.