Postgres NO en matriz

96

Estoy usando el tipo de matriz nativa de Postgres y trato de encontrar los registros donde la ID no está en las ID de destinatario de la matriz.

Puedo encontrar dónde están EN:

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

Pero esto no funciona:

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

¿Cuál es la forma correcta de evaluar esta afección?

usuario577808
fuente
hace WHERE 3 NOT IN recipient_idsel trabajo?
Janus Troelsen
1
Nota relacionada: en cuanto a text[]y int[]matriz:select not(array[1,2,3] @> array[3]);
Steve Peak
3
Consejo profesional: si está comprobando si una nullcolumna está contenida o no en una matriz, siempre dirá que no. Me tomó como 20 minutos depurar varios métodos de contenido para llegar a la conclusión de que no se puede verificar si una matriz contiene un valor nulo
André Pena

Respuestas:

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

Siempre puedes negar WHERE (condition)conWHERE NOT (condition)

Frank Farmer
fuente
2
@aschyiel - Es posible que desee volver a en ANYlugar de a INmedida que recipient_idscrece su lista de entrada: stackoverflow.com/questions/1009706/…
derekm
39

Podría darle la vuelta un poco y decir "3 no es igual a todos los ID":

where 3 != all (recipient_ids)

Del manual fino :

9.21.4. TODOS (matriz)

expression operator ALL (array expression)

El lado derecho es una expresión entre paréntesis, que debe producir un valor de matriz. La expresión de la izquierda se evalúa y se compara con cada elemento de la matriz utilizando el operador dado , que debe producir un resultado booleano. El resultado de ALLes "verdadero" si todas las comparaciones dan como resultado verdadero (incluido el caso en el que la matriz tiene cero elementos). El resultado es "falso" si se encuentra algún resultado falso.

mu es demasiado corto
fuente
esto realmente no explica por qué anyno funciona en este caso
seanlinsley
Esto debe aceptarse ya que explica el motivo correctamente. PD también puede encontrar anyy allen postgres doc, que dice: " x <> ANY (a,b,c) es equivalente a x <> a OR <> b OR x <> c". ref: postgresqltutorial.com/postgresql-any postgresqltutorial.com/postgresql-all
Tyler Temp
19

Aumento de las ALL/ANYrespuestas

Prefiero todas las soluciones que utilicen allo anypara lograr el resultado, apreciando las notas adicionales (por ejemplo, sobre NULL s). Como otra mejora, aquí hay una forma de pensar en esos operadores.

Puede pensar en ellos como operadores de cortocircuito :

  • all(array)recorre todos los valores de la matriz, comparando cada uno con el valor de referencia utilizando el operador proporcionado. Tan pronto como se obtiene una comparación false, el proceso termina con falso, de lo contrario verdadero. (Comparable a la lógica de cortocircuito and).
  • any(array)recorre todos los valores de la matriz, comparándolos con el valor de referencia utilizando el operador proporcionado. Tan pronto como se obtiene una comparación true, el proceso termina con verdadero, de lo contrario falso. (Comparable a la lógica de cortocircuito or).

Esta es la razón por la 3 <> any('{1,2,3}')que no produce el resultado deseado: el proceso compara 3 con 1 para la desigualdad, que es verdadero, e inmediatamente devuelve verdadero. Un solo valor en la matriz diferente de 3 es suficiente para que toda la condición sea verdadera. El 3 en la última posición de la matriz es prob. nunca usado.

3 <> all('{1,2,3}')por otro lado, se asegura de que todos los valores no sean iguales 3. Se ejecutarán todas las comparaciones que arrojen verdadero hasta un elemento que arroje falso (el último en este caso), para devolver falso como resultado general. Esto es lo que quiere el OP.

ThomasH
fuente
12

not (3 = any(recipient_ids))?

Markus Mikkolainen
fuente
Gracias, estaba usando 3 <> ANY(ARRAY[1,2,3,4]). Debería haber funcionado de esa manera: \
yeyo
11

una actualización:

a partir de postgres 9.3,

también puede usarlo NOTen conjunto con el @> operador (contiene) para lograr esto.

ES DECIR.

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

Gallo
fuente
11

Cuidado con los NULL

Ambos ALL:

(some_value != ALL(some_array))

Y ANY:

NOT (some_value = ANY(some_array))

Funcionaría siempre que some_arrayno sea nulo. Si la matriz puede ser nula, debe contabilizarla con coalesce (), por ejemplo

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

O

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

De los documentos :

Si la expresión de la matriz produce una matriz nula, el resultado de ANY será nulo

Si la expresión de la matriz produce una matriz nula, el resultado de TODOS será nulo

isapir
fuente
3

Tenga en cuenta que los operadores ANY / ALL no funcionarán con índices de matriz. Si se tienen en cuenta los índices:

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

y lo negativo:

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

Luego se puede crear un índice como:

CREATE INDEX recipient_ids_idx on tableName USING GIN(recipient_ids)
jamming james
fuente
A diferencia de otras respuestas, esta respuesta en realidad hace uso del operador de superposición de matriz de PostgreSQL. &&
Ceiling Gecko
6
Esto no funcionará como está escrito. Los operadores de matriz como && y @> requieren que ambos elementos sean matrices, que 3 no lo es. Para que esto funcione, sería necesario que la consulta puede escribir como: SELECT COUNT(*) FROM "messages" WHERE ARRAY[3] && recipient_ids.
Dologan