PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Existe uma abordagem padrão para lidar com arrays não ordenados (conjuntos) no PostgreSQL?


Não há nenhuma maneira interna neste momento.

Como matrizes


Se você normalizá-los consistentemente ao salvar, poderá tratar os arrays como conjuntos, armazenando-os sempre classificados e desduplicados. Seria ótimo se o PostgreSQL tivesse uma função C embutida para fazer isso, mas não tem. Eu dei uma olhada em escrever um, mas a API de matriz C é horrível , então mesmo que eu tenha escrito um monte de extensões, eu apenas me afastei cuidadosamente desta.

Se você não se importa com o desempenho moderadamente nocivo, pode fazê-lo no SQL:
CREATE OR REPLACE FUNCTION array_uniq_sort(anyarray) RETURNS anyarray AS $$
SELECT array_agg(DISTINCT f ORDER BY f) FROM unnest($1) f;
$$ LANGUAGE sql IMMUTABLE;

em seguida, envolva todos os salvamentos em chamadas para array_uniq_sort ou aplicá-lo com um gatilho. Você pode então apenas comparar seus arrays para igualdade. Você pode evitar o array_uniq_sort solicita dados do aplicativo se, em vez disso, você apenas fez a classificação/exclusivo no lado do aplicativo.

Se você fizer isso por favor armazene seus "conjuntos" como colunas de matriz, como text[] , não texto delimitado por vírgula ou espaço. Consulte esta pergunta por alguns dos motivos.

Você precisa tomar cuidado com algumas coisas, como o fato de que as conversões entre arrays são mais rígidas do que as conversões entre seus tipos base. Por exemplo.:
regress=> SELECT 'a' = 'a'::varchar, 'b' = 'b'::varchar;
 ?column? | ?column? 
----------+----------
 t        | t
(1 row)

regress=> SELECT ARRAY['a','b'] = ARRAY['a','b']::varchar[];
ERROR:  operator does not exist: text[] = character varying[]
LINE 1: SELECT ARRAY['a','b'] = ARRAY['a','b']::varchar[];
                              ^
HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
regress=> SELECT ARRAY['a','b']::varchar[] = ARRAY['a','b']::varchar[];
 ?column? 
----------
 t
(1 row)

Essas colunas são indexáveis ​​por GiST para operações como array-contém ou array-overlaps; veja a documentação do PostgreSQL sobre indexação de array.

Como linhas normalizadas


A outra opção é apenas armazenar linhas normalizadas com uma chave adequada. Eu ainda usaria array_agg para classificá-los e compará-los, pois as operações de conjunto SQL podem ser desajeitadas de usar para isso (especialmente devido à falta de uma operação de diferença de conjunto XOR / dupla face).

Isso é geralmente conhecido como EAV (entity-attribute-value). Eu mesmo não sou fã, mas ele tem seu lugar ocasionalmente. Exceto que você o usaria sem o value componente.

Você cria uma tabela:
CREATE TABLE item_attributes (
    item_id integer references items(id),
    attribute_name text,
    primary key(item_id, attribute_name)
);

e insira uma linha para cada entrada de conjunto para cada item, em vez de cada item ter uma coluna com valor de matriz. A restrição exclusiva imposta pela chave primária garante que nenhum item possa ter duplicatas de um determinado atributo. A ordenação dos atributos é irrelevante/indefinida.

As comparações podem ser feitas com operadores de conjunto SQL como EXCEPT , ou usando array_agg(attribute_name ORDER BY attribute_name) para formar matrizes classificadas de forma consistente para comparação.

A indexação limita-se a determinar se um determinado item possui ou não um determinado atributo.

Pessoalmente, eu usaria matrizes sobre essa abordagem.

hstore


Você também pode usar hstores com valores vazios para armazenar conjuntos, pois hstore desduplica chaves. jsonb da versão 9.4 também funcionará para isso.
regress=# create extension hstore;
CREATE EXTENSION
regress=# SELECT hstore('a => 1, b => 1') = hstore('b => 1, a => 1, b => 1');
 ?column? 
----------
 t
(1 row)

Só é realmente útil para tipos de texto, no entanto. por exemplo.:
regress=# SELECT hstore('"1.0" => 1, "2.0" => 1') = hstore('"1.00" => 1, "1.000" => 1, "2.0" => 1');
 ?column? 
----------
 f
(1 row)

e acho feio. Então, novamente, eu prefiro matrizes.

Somente para matrizes inteiras


O intarray A extensão fornece funções úteis e rápidas para tratar arrays como conjuntos. Eles estão disponíveis apenas para arrays inteiros, mas são realmente úteis.