Mesclagem eficiente (remoção de duplicatas) de matrizes

10

Eu tenho duas mesas left2e right2. Ambas as tabelas serão grandes (de 1 a 10 milhões de linhas).

CREATE TABLE left2(id INTEGER, t1 INTEGER, d INTEGER);
ALTER TABLE left2 ADD PRIMARY KEY (id,t1);

CREATE TABLE right2( t1 INTEGER, d INTEGER, arr INTEGER[] );
ALTER TABLE right2 ADD PRIMARY KEY(t1,d);

Vou executar este tipo de consulta:

SELECT l.d + r.d,
       UNIQ(SORT((array_agg_mult(r.arr)))
FROM left2 l,
     right2 r
WHERE l.t1 = r.t1
GROUP BY l.d + r.d
ORDER BY l.d + r.d;

Onde para agregação de matrizes eu uso a função:

CREATE AGGREGATE array_agg_mult(anyarray) (
SFUNC=array_cat,
STYPE=anyarray,
INITCOND='{}');

Depois de concatenar as matrizes, eu uso a UNIQfunção do intarraymódulo. Existe uma maneira mais eficiente de fazer isso? Existe algum índice no arrcampo para acelerar a fusão (com a remoção de duplicatas)? A função agregada pode remover duplicatas diretamente? Matrizes originais podem ser consideradas classificadas (e são únicas) se isso ajudar.

O SQL Fiddle está aqui :

Alexandros
fonte
Você vai consultar milhões de linhas de uma só vez? O que você está fazendo com o resultado? Ou haverá predicados para selecionar alguns? Pode right2.arr ser NULL como sugere seu esquema demo? Você precisa de matrizes classificadas como resultado?
Erwin Brandstetter

Respostas:

9

Resultados corretos?

Primeiro: correção. Deseja produzir uma matriz de elementos únicos? Sua consulta atual não faz isso. A função uniq()do módulo intarray promete apenas:

remover duplicatas adjacentes

Como instruído no manual , você precisaria de:

SELECT l.d + r.d, uniq(sort(array_agg_mult(r.arr)))
FROM   ...

Também fornece matrizes ordenadas - supondo que você queira isso, você não esclareceu.

Vejo que você tem sort() no seu violino , então isso pode ser apenas um erro de digitação na sua pergunta.

Postgres 9.5

De qualquer forma, você vai adorar o novo Postgres 9.5 (atualmente beta). Ele fornece os recursos prontos array_agg_mult()para uso e muito mais rápido:

Também houve outras melhorias de desempenho para manipulação de array.

Inquerir

O principal objetivo de array_agg_mult()é agregar matrizes multidimensionais, mas você produz apenas matrizes unidimensionais de qualquer maneira. Então, pelo menos, tentaria esta consulta alternativa:

SELECT l.d + r.d AS d_sum, array_agg(DISTINCT elem) AS result_arr
FROM   left2  l
JOIN   right2 r USING (t1)
     , unnest(r.arr) elem
GROUP  BY 1
ORDER  BY 1;

O que também aborda sua pergunta:

A função agregada pode remover duplicatas diretamente?

Sim, pode, com DISTINCT. Mas isso não é mais rápido do que uniq()para matrizes inteiras, que foi otimizado para matrizes inteiras, enquanto DISTINCTé genérico para todos os tipos de dados qualificados.

Não requer o intarraymódulo. No entanto , o resultado não é necessariamente classificado. O Postgres usa algoritmos variados para DISTINCT(IIRC), grandes conjuntos geralmente são divididos em hash e o resultado não é classificado, a menos que você adicione explícito ORDER BY. Se você precisar de matrizes classificadas, poderá adicionar ORDER BYdiretamente à função agregada:

array_agg(DISTINCT elem ORDER BY elem)

Mas isso normalmente é mais lento do que a alimentação de dados pré-classificados array_agg()(uma grande classificação versus muitas pequenas). Então, eu classificaria em uma subconsulta e depois agregaria:

SELECT d_sum, uniq(array_agg(elem)) AS result_arr
FROM  (
   SELECT l.d + r.d AS d_sum, elem
   FROM   left2  l
   JOIN   right2 r USING (t1)
        , unnest(r.arr) elem
   ORDER  BY 1, 2
   ) sub
GROUP  BY 1
ORDER  BY 1;

Essa foi a variante mais rápida no meu teste superficial no Postgres 9.4.

SQL Fiddle com base no que você forneceu.

Índice

Não vejo muito potencial para nenhum índice aqui. A única opção seria:

CREATE INDEX ON right2 (t1, arr);

Só faz sentido se você conseguir verificações apenas de índice - o que acontecerá se a tabela subjacente right2for substancialmente mais ampla do que apenas essas duas colunas e sua instalação se qualificar para verificações apenas de índice. Detalhes no Wiki do Postgres.

Erwin Brandstetter
fonte
Obrigado +1. De qualquer forma, terei que UNNEST mais tarde, mas quero verificar se a remoção de duplicatas nas matrizes e UNNEST é mais rápida.
Alexandros
0

Estou muito decepcionado, isso é uma coisa fácil de fazer no Microsoft Access. Você pode criar uma consulta "remover duplicados" e, em seguida, examinar o SQL para ver como ele está fazendo isso. Vou ter que iniciar uma máquina Windows para olhar. Eles variam, o assistente de consulta faz isso.

Acho que uma coisa que funciona é carregar todos os seus dados em uma tabela e, em seguida, SELECT DISTINCT em uma nova tabela. Você também pode manter uma ordem por cláusula enquanto estiver nisso. Eu fiz isso de alguma forma um ano atrás, deve ser isso.

Estou combinando 2 anos de dados de temperatura, o sensor envia 2 cópias do mesmo ponto de dados a cada minuto como uma proteção redundante. Às vezes, alguém fica na lixeira, mas eu só quero ficar com um. Eu também tenho sobreposições entre arquivos.

Se os dados tiverem exatamente o mesmo formato durante toda a execução, em uma máquina unix, você poderá fazer algo como

cat *.tab > points.txt
sort -n < points.txt > sorted.txt
uniq -u sorted.txt unique.txt

Mas o uniq compara linhas como strings e, por exemplo, 18.7000 não é o mesmo que 18.7. Eu mudei meu software durante os 2 anos, por isso tenho os dois formatos.

Alan Corey
fonte
Decepcionado com o Postgres? O Access tem matrizes?
ypercubeᵀᴹ
Não sei, mas ele pode remover duplicatas, é um problema bastante comum na limpeza de dados. Selecionar distinto é próximo o suficiente. Você nem sempre tem controle sobre seus dados brutos do mundo real.
27618 Alan Corey