Erro no tamanho máximo da linha do índice

12

Existe um limite superior para uma arraycoluna?

Estou recebendo esse erro ao inserir no campo array -

PG::Error: ERROR:  index row size 3480 exceeds maximum 2712 for index "ix_data"

Aqui está a minha definição de tabela -

create table test_array(id varchar(50), data text[]);

ALTER TABLE test_array ADD PRIMARY KEY (id);

CREATE INDEX ix_data ON test_array USING GIN (data);

Preciso de um índice no campo array, pois estou fazendo algumas pesquisas nele.


fonte
Será que datacontém uma lista de tags como demonstradas nesta postagem de blog relacionada por Scott Snyder ? Se for esse o caso, talvez eu tenha uma solução melhor para você.
Erwin Brandstetter
user310525, gostaria de confirmar a sugestão de Erwin de que isso seria melhor no dba.se, se você estiver disposto a criar uma conta lá e sinalizar para a migração de um moderador?
Jack diz que tente topanswers.xyz

Respostas:

14

O problema

Aqui está um caso muito semelhante discutido no pgsql.general . É sobre a limitação em um índice de árvore b, mas é o mesmo porque um índice GIN usa um índice de árvore b internamente para chaves e, portanto, é executado na mesma limitação para o tamanho da chave (em vez do tamanho do item em uma árvore b simples índice).

Cito o manual sobre a implementação do índice GIN :

Internamente, um índice GIN contém um índice de árvore B construído sobre chaves, em que cada chave é um elemento de um ou mais itens indexados

De qualquer forma, pelo menos um elemento da matriz em sua coluna dataé muito grande para ser indexado. Se esse for apenas um valor estranho ou algum tipo de acidente, você poderá truncar o valor e concluir o processo.

Para os fins da seguinte demonstração, assumirei o contrário: muitos valores de texto longo na matriz.

Solução simples

Você pode substituir elementos em sua matriz datapor valores de hash correspondentes . E envie valores de pesquisa através da mesma função hash. Obviamente, você provavelmente deseja armazenar seus originais em algum lugar. Com isso, quase chegamos à minha segunda variante ...

Solução avançada

Você pode criar uma tabela de consulta para elementos de matriz com uma serialcoluna como chave primária substituta (efetivamente um tipo radical de valor de hash) - o que é ainda mais interessante se os valores dos elementos envolvidos não forem exclusivos:

CREATE TABLE elem (
  elem_id serial NOT NULL PRIMARY KEY
, elem    text UNIQUE NOT NULL
);

Como queremos procurar elem, adicionamos um índice - mas um índice a uma expressão dessa vez, com apenas os 10 primeiros caracteres do texto longo. Isso deve ser suficiente na maioria dos casos para restringir uma pesquisa a um ou alguns hits. Adapte o tamanho à sua distribuição de dados. Ou use uma função hash mais sofisticada.

CREATE INDEX elem_elem_left10_idx ON elem(left(elem,10));

Sua coluna dataseria do tipo int[]. Renomeei a mesa datae me livrei do sinistro que varchar(50)você tinha no seu exemplo:

CREATE TEMP TABLE data(
  data_id serial PRIMARY KEY
, data int[]
);

Cada elemento da matriz datarefere-se a a elem.elem_id. Nesse ponto, você pode considerar substituir a coluna da matriz por uma tabela n: m, normalizando assim seu esquema e permitindo que o Postgres imponha integridade referencial. A indexação e o manuseio geral se tornam mais fáceis ...

No entanto, por razões de desempenho, a int[]coluna em combinação com um índice GIN pode ser superior. O tamanho do armazenamento é muito menor. Nesse caso, precisamos do índice GIN:

CREATE INDEX data_data_gin_idx ON data USING GIN (data);

Agora, cada chave do índice GIN (= elemento da matriz) é um em integervez de um longo text. O índice será menor em várias ordens de magnitude; as pesquisas, consequentemente, serão muito mais rápidas.

A desvantagem: antes que você possa realmente fazer uma pesquisa, é necessário procurar elem_idna tabela elem. Usar o meu índice funcional recém-introduzido elem_elem_left10_idxtambém será muito mais rápido.

Você pode fazer tudo isso em uma consulta simples :

SELECT d.*, e.*
FROM   elem e
JOIN   data d ON ARRAY[e.elem_id] <@ d.data
WHERE  left(e.elem, 10) = left('word1234word', 10) -- match index condition
AND    e.elem = 'word1234word';  -- need to recheck, functional index is lossy

Você pode estar interessado na extensão intarray, que fornece operadores e classes de operadores adicionais.

Demonstração ao vivo totalmente funcional no sqlfiddle.

Erwin Brandstetter
fonte
2

O erro está no índice ix_data, não no text[]campo. O tamanho máximo de uma linha nesse tipo de índice específico é limitado a 2712bytes. Se você soltar seu índice e tentar inserir novamente, ele deverá funcionar para você. Se você precisar indexar um campo maior, convém examinar os recursos de indexação de texto completo do postgres.

jcern
fonte
2

Eu estava recebendo isso em uma coluna de geografia do PostGIS. Foi porque eu acidentalmente criei o índice incorretamente. Você deve incluir o parâmetro USING GIST ao criar esses índices.

Brad Mathews
fonte
Obrigado - foi isso! Uau, até agora buscado. Pode ter me poupado horas. Especialmente porque eu pensei que o GiST era usado por padrão, mas eu estava enganado e ele tenta usar o b-tree.
Jonas