2012-10-13 24 views
11

Ho una tabella con 4 colonne di matrice ignorando .. i risultati sono simili:Confronto array di uguaglianza, ordine degli elementi

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

Comunque per confrontare ids e signed_ids modo che risulti uguale, ignorando la ordine degli elementi?

+1

Quei array non sono uguali. Ho ragione ad indovinare che la tua vera domanda è "come faccio a confrontare due array PostgreSQL come se fossero insiemi, cioè senza rispettare l'ordine?" –

+0

Sì, è esattamente quello che sto chiedendo :) – user766987

+0

Domanda modificata per riflettere l'intenzione. –

risposta

9

La cosa più semplice da fare è ordinarli e confrontarli in ordine. Vedi sorting arrays in PostgreSQL.

Dato dati di esempio:

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

la cosa migliore da fare è se le voci di matrice sono sempre numeri interi è quello di utilizzare l'estensione intArray, come Erwin explains in his answer. È un lotto più veloce di qualsiasi formulazione pura SQL.

In caso contrario, per una versione generale che funziona per qualsiasi tipo di dati, definire 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'; 

e usarlo ordinare e confrontare le matrici ordinate:

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

c'è un avvertimento importante:

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

sarà falso. Questo può o non può essere quello che vuoi, a seconda delle tue intenzioni.


alternativa, definire una funzione 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; 

e quindi:

SELECT array_compare_as_set(ids, signed_ids) FROM aa; 

Questo è leggermente diverso da confrontare due valori array_sort ed. array_compare_as_set eliminerà i duplicati, rendendo array_compare_as_set(ARRAY[1,2,3,3],ARRAY[1,2,3]) vero, mentre array_sort(ARRAY[1,2,3,3]) = array_sort(ARRAY[1,2,3]) sarà falso.

Entrambi questi approcci avranno prestazioni piuttosto negative. Prendi in considerazione la possibilità di archiviare sempre gli array ordinati in primo luogo.

+0

Aggiungi un alias al tuo tavolo e geniale! Grazie :) – user766987

+0

@ user766987 Aggiornato con una definizione migliore –

+0

@ user766987 ... e aggiornato di nuovo con un approccio diverso, solo per divertimento. –

12

Per gestire gli array dell'intero è possibile installare l'estensione intarray.

Installare una volta al database con (in Postgres 9.1 o successiva):

CREATE EXTENSION intarray; 

allora si può solo:

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

Oppure:

SELECT ids @> signed_ids AND ids <@ signed_ids;

Bold l'accento sulle funzioni e operatori da intarray. Entrambe le espressioni ignoreranno l'ordine e la duplicità di elementi. Ulteriori informazioni su queste funzioni e operatori nel manuale utile here.

Note:

  • intarray operatori funzionano solo per gli array di integer, non bigint o smallint o qualsiasi altro tipo di dati.
  • È possibile utilizzare gli operatori di contenimento @> e <@ senza installare intarray perché esistono varianti generiche per i tipi di array nella distribuzione Postgres standard. intarray installa solo operatori specializzati per int[], che sono in genere più veloci.
  • A differenza degli operatori generici, quelli intarray non accettano valori NULL negli array, il che può creare confusione: ora si ottiene un messaggio di errore se si dispone di NULL in qualsiasi array coinvolto.
    Se avete bisogno di lavorare con i valori NULL, è possibile predefinito allo standard, gli operatori generici da schema-qualifica l'operatore con il OPERATOR costrutto:

    SELECT ARRAY[1,4,null,3]::int[] OPERATOR([email protected]>) ARRAY[3,1]::int[]
  • Gli operatori generici non possono usare gli indici con una classe operatore intarray e viceversa.

+0

Anche 'intarray' funziona con gli array' bigint'? – ma11hew28

+0

È la prima soluzione (usando 'sort') più veloce della seconda (usando' @> 'e' <@ ')? Se no, immagino di poter fare a meno di 'intarray'. – ma11hew28

+1

@mattdipasquale: ho aggiunto qualche altra spiegazione. –

6

È possibile utilizzare contenuti da parte dell'operatore:

(matrice1 < @ matrice2 e matrice1 @> matrice2)