2011-06-30 8 views

Respuesta

2

El módulo contrib/intarray proporciona esta funcionalidad - para las matrices de números enteros, de todos modos. Para otros tipos de datos, puede que tenga que escribir sus propias funciones (o modificar las que se proporcionan con intarray).

17
select array_agg(elements) 
from (
    select unnest(array[12,3,5,7,8]) 
    except 
    select unnest(array[3,7,8]) 
) t (elements) 
13

Me acercaría a esto con el operador de matriz.

select array(select unnest(:arr1) except select unnest(:arr2)); 

Si: arr1 y: arr2 no se cruzan, utilizando ARRAY_AGG() conduce a un valor nulo.

+0

El orden de las matrices produce diferentes resultados aquí: seleccionar matriz (seleccionar unnest (ARRAY ['1']) excepto seleccionar unnest (ARRAY ['1', '2'])) devuelve lista vacía pero seleccionar matriz (seleccionar unnest (ARRAY ['1', '2 ']) excepto select unnest (ARRAY [' 1 '])) devuelve {2}. – Brady

+4

@ Brady: como debería, ¿no? '{1} - {1,2} = {}', '{1,2} - {1} = {2}'. –

+0

Solo para resaltar que esta función no es estable, el orden en ': arr1' no se conserva. Gracias por compartir este one-liner. – jlandercy

7

Probemos el UNNEST()/excepto:

EXPLAIN ANALYZE SELECT array(select unnest(ARRAY[1,2,3,n]) EXCEPT SELECT unnest(ARRAY[2,3,4,n])) FROM generate_series(1,10000) n; 
Function Scan on generate_series n (cost=0.00..62.50 rows=1000 width=4) (actual time=1.373..140.969 rows=10000 loops=1) 
    SubPlan 1 
    -> HashSetOp Except (cost=0.00..0.05 rows=1 width=0) (actual time=0.011..0.011 rows=1 loops=10000) 
      -> Append (cost=0.00..0.04 rows=2 width=0) (actual time=0.002..0.008 rows=8 loops=10000) 
       -> Subquery Scan "*SELECT* 1" (cost=0.00..0.02 rows=1 width=0) (actual time=0.002..0.003 rows=4 loops=10000) 
         -> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.002 rows=4 loops=10000) 
       -> Subquery Scan "*SELECT* 2" (cost=0.00..0.02 rows=1 width=0) (actual time=0.001..0.003 rows=4 loops=10000) 
         -> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.001..0.002 rows=4 loops=10000) 
Total runtime: 142.531 ms 

Y el intArray operador especial:

EXPLAIN ANALYZE SELECT ARRAY[1,2,3,n] - ARRAY[2,3,4,n] FROM generate_series(1,10000) n; 
Function Scan on generate_series n (cost=0.00..15.00 rows=1000 width=4) (actual time=1.338..11.381 rows=10000 loops=1) 
Total runtime: 12.306 ms 

Línea de base:

EXPLAIN ANALYZE SELECT ARRAY[1,2,3,n], ARRAY[2,3,4,n] FROM generate_series(1,10000) n; 
Function Scan on generate_series n (cost=0.00..12.50 rows=1000 width=4) (actual time=1.357..7.139 rows=10000 loops=1) 
Total runtime: 8.071 ms 

Tiempo por conjunto de intersección:

intarray -   : 0.4 µs 
unnest()/intersect : 13.4 µs 

supuesto, la forma intArray es mucho más rápido, pero me resulta sorprendente que Postgres pueden zap una subconsulta dependientes (que contiene un hash y otras cosas) en 13,4 mu s ...

6

Yo he construido un conjunto de funciones para tratar específicamente con este tipo de problemas: https://github.com/JDBurnZ/anyarray

Lo mejor es que estas funciones funcionan en TODOS los tipos de datos, no solo enteros, como intarray se limita a.

Después de cargar la carga de las funciones definidas en los archivos SQL desde GitHub, todo lo que se necesita hacer es:

SELECT 
    ANYARRAY_DIFF(
    ARRAY[12, 3, 5, 7, 8], 
    ARRAY[3, 7, 8] 
) 

devoluciones algo similar a: ARRAY[12, 5]

Si también necesita devolver el los valores ordenados:

SELECT 
    ANYARRAY_SORT(
    ANYARRAY_DIFF(
     ARRAY[12, 3, 5, 7, 8], 
     ARRAY[3, 7, 8] 
    ) 
) 

devoluciones exactamente: ARRAY[5, 12]

+2

¡Buen trabajo! ¡gracias por compartir! –

0

me gustaría crear una función utilizando la misma lógica, excepto según lo descrito por @a_horse_with_no_name:

CREATE FUNCTION array_subtract(a1 int[], a2 int[]) RETURNS int[] AS $$ 
DECLARE 
    ret int[]; 
BEGIN 
    IF a1 is null OR a2 is null THEN 
     return a1; 
    END IF; 
    SELECT array_agg(e) INTO ret 
    FROM (
     SELECT unnest(a1) 
     EXCEPT 
     SELECT unnest(a2) 
    ) AS dt(e); 
    RETURN ret; 
END; 
$$ language plpgsql; 

continuación, puede utilizar esta función para cambiar la variable de base_array en consecuencia:

base_array := array_subtract(base_array, temp_array); 
Cuestiones relacionadas