2012-10-13 161 views
11

Tengo una tabla con 4 columnas de la matriz .. ignorando los resultados son como:comparar arrays para la igualdad, el orden de los elementos

ids  signed_ids new_ids new_ids_signed 
{1,2,3} | {2,1,3} | {4,5,6} | {6,5,4} 

De todas formas para comparar ids y signed_ids para que salgan iguales, haciendo caso omiso de la orden de los elementos?

+1

Esas matrices no son iguales. ¿Estoy en lo cierto al adivinar que su verdadera pregunta es "¿cómo puedo comparar dos matrices PostgreSQL como si fueran conjuntos, es decir, sin respetar el orden?" –

+0

Sí, eso es exactamente lo que estoy pidiendo :) – user766987

+0

Pregunta editada para reflejar la intención. –

Respuesta

9

Lo más simple es ordenarlos y compararlos ordenados. Ver sorting arrays in PostgreSQL.

Teniendo en cuenta los datos de la muestra:

CREATE TABLE aa(ids integer[], signed_ids integer[]); 
INSERT INTO aa(ids, signed_ids) VALUES (ARRAY[1,2,3], ARRAY[2,1,3]); 

lo mejor que puede hacer es si las entradas de la matriz son siempre números enteros es usar la extensión intArray, como Erwin explains in his answer. Es un lote más rápido que cualquier formulación de SQL puro.

De lo contrario, para una versión general de que funciona para cualquier tipo de datos, definir un array_sort(anyarray):

CREATE OR REPLACE FUNCTION array_sort(anyarray) RETURNS anyarray AS $$ 
SELECT array_agg(x order by x) FROM unnest($1) x; 
$$ LANGUAGE 'SQL'; 

y usarlo ordenar y comparar las matrices Ordenada:

SELECT array_sort(ids) = array_sort(signed_ids) FROM aa; 

Hay una advertencia importante:

SELECT array_sort(ARRAY[1,2,2,4,4]) = array_sort(ARRAY[1,2,4]); 

serán falsos. Esto puede o no ser lo que quieras, dependiendo de tus intenciones.


Alternativamente, definir una función array_compare_as_set:

CREATE OR REPLACE FUNCTION array_compare_as_set(anyarray,anyarray) RETURNS boolean AS $$ 
SELECT CASE 
    WHEN array_dims($1) <> array_dims($2) THEN 
    'f' 
    WHEN array_length($1,1) <> array_length($2,1) THEN 
    'f' 
    ELSE 
    NOT EXISTS (
     SELECT 1 
     FROM unnest($1) a 
     FULL JOIN unnest($2) b ON (a=b) 
     WHERE a IS NULL or b IS NULL 
    ) 
    END 
$$ LANGUAGE 'SQL' IMMUTABLE; 

y luego:

SELECT array_compare_as_set(ids, signed_ids) FROM aa; 

Esto es sutilmente diferente de la comparación de dos valores array_sort ed. array_compare_as_set eliminará duplicados, haciendo que array_compare_as_set(ARRAY[1,2,3,3],ARRAY[1,2,3]) sea verdadero, mientras que array_sort(ARRAY[1,2,3,3]) = array_sort(ARRAY[1,2,3]) será falso.

Ambos enfoques tendrán un rendimiento bastante malo. Considere asegurarse de que siempre almacena sus matrices ordenadas en primer lugar.

+0

¡Agregue un alias a su mesa y brillante! Gracias :) – user766987

+0

@ user766987 Actualizado con una mejor definición –

+0

@ user766987 ... y actualizado de nuevo con un enfoque diferente, solo por diversión. –

12

Tratando con matrices del número entero puede instalar la extensión intarray.

Instalar una vez por base de datos con (en Postgres 9.1 o posterior):

CREATE EXTENSION intarray; 

a continuación, puedes:

SELECT uniq(sort(ids)) = uniq(sort(signed_ids));

O:

SELECT ids @> signed_ids AND ids <@ signed_ids;

énfasis en negrilla en las funciones y operadores de intarray. Ambas expresiones ignorarán el orden y la duplicidad de los elementos. Obtenga más información acerca de estas funciones y operadores en el útil manual here.

Notas:

  • intarray operadores sólo el trabajo para las matrices de integer, no bigint o smallint o cualquier otro tipo de datos.
  • Puede usar los operadores de contención @> y <@ sin instalar intarray porque hay variantes genéricas para los tipos de matriz en la distribución estándar de Postgres. intarray instala operadores especializados solo para int[], que suelen ser más rápidos.
  • A diferencia de los operadores genéricos, los intarray no aceptan valores NULOS en las matrices, lo que puede ser confuso: ahora se obtiene un mensaje de error si tiene NULL en cualquier matriz involucrada.
    Si tiene que trabajar con valores NULL, puede por defecto a la norma, los operadores genéricos por esquema de calificación del operador con el constructo OPERATOR:

    SELECT ARRAY[1,4,null,3]::int[] OPERATOR([email protected]>) ARRAY[3,1]::int[]
  • Los operadores genéricos no pueden usar índices con una clase de operador intarray y viceversa.

+0

¿'intarray' funciona también para matrices' bigint'? – ma11hew28

+0

¿Es la primera solución (usando 'sort') más rápida que la segunda (usando' @> 'y' <@ ')? Si no, creo que puedo prescindir de 'intarray'. – ma11hew28

+1

@mattdipasquale: Agregué más explicación. –

6

Usted puede utilizar contenida por el operador:

(array1 < @ matriz2 y array1 @> matriz2)

Cuestiones relacionadas