Ошибка индекса максимального размера строки

12

Есть ли верхняя граница для arrayстолбца?

Я получаю эту ошибку при вставке в поле массива -

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

Вот мое определение таблицы -

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);

Мне нужен индекс для поля массива, так как я делаю некоторые поиски по нему.


источник
Может быть, он dataсодержит список тегов, как показано в этом сообщении в блоге Скотта Снайдера ? Если это так, у меня может быть лучшее решение для вас.
Эрвин Брандштеттер,
user310525, я хотел бы поддержать предположение Эрвина, что на dba.se это будет лучше, если вы хотите создать там учетную запись и пометить модератора для миграции?
Джек говорит, что попробуйте topanswers.xyz

Ответы:

14

Эта проблема

Вот очень похожий случай, обсуждаемый на pgsql.general . Речь идет об ограничении в индексе b-дерева, но это все то же самое, потому что индекс GIN внутренне использует индекс b-дерева для ключей и, следовательно, сталкивается с тем же ограничением для размера ключа (вместо размера элемента в простом b-дереве). показатель).

Я цитирую руководство по реализации индекса GIN :

Внутри индекс GIN содержит индекс B-дерева, построенный по ключам, где каждый ключ является элементом одного или нескольких проиндексированных элементов.

В любом случае, по крайней мере один элемент массива в вашем столбце dataслишком велик для индексации. Если это просто единичное значение или какая-то случайность, вы можете усечь значение и покончить с этим.

Для следующей демонстрации я буду предполагать иначе: множество длинных текстовых значений в массиве.

Простое решение

Вы можете заменить элементы в вашем массиве dataсоответствующими значениями хеша . И отправлять значения поиска через ту же хеш-функцию. Конечно, вы, вероятно, хотите где-то хранить свои оригиналы. С этим мы почти приходим ко второму варианту ...

Усовершенствованное решение

Вы можете создать справочную таблицу для элементов массива со serialстолбцом в качестве суррогатного первичного ключа (фактически это радикальный вид хеш-значения), что тем более интересно, если значения участвующих элементов не являются уникальными:

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

Так как мы хотим , чтобы посмотреть elem, мы добавим индекс - но в индекс на выражении на этот раз, и только первые 10 символов длинного текста. В большинстве случаев этого должно быть достаточно, чтобы сузить поиск до одного или нескольких обращений. Адаптируйте размер к вашему распределению данных. Или используйте более сложную хэш-функцию.

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

Ваш столбец dataбудет иметь тип int[]. Я переименовал стол в dataи избавился от зловещего, что varchar(50)вы имели в своем примере:

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

Каждый элемент массива в dataотносится к elem.elem_id. На этом этапе вы можете рассмотреть возможность замены столбца массива таблицей n: m, тем самым нормализуя вашу схему и позволяя Postgres обеспечивать ссылочную целостность. Индексирование и общая обработка становятся проще ...

Однако по соображениям производительности int[]столбец в сочетании с индексом GIN может быть лучше. Размер хранилища намного меньше. В этом случае нам нужен индекс GIN:

CREATE INDEX data_data_gin_idx ON data USING GIN (data);

Теперь каждый ключ индекса GIN (= элемент массива) integerвместо длинного text. Индекс будет меньше на несколько порядков, следовательно, поиск будет намного быстрее.

Недостаток: прежде чем вы действительно сможете выполнить поиск, вы должны посмотреть в elem_idтаблице elem. Используя мой недавно введенный функциональный индекс elem_elem_left10_idx, это тоже будет намного быстрее.

Вы можете сделать все это одним простым запросом :

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

Возможно, вас заинтересует расширение intarray, которое предоставляет дополнительные операторы и классы операторов.

Полнофункциональная живая демонстрация на sqlfiddle.

Эрвин Брандштеттер
источник
2

Ошибка с индексом ix_data, а не с text[]полем. Максимальный размер строки в этом конкретном типе индекса ограничен 2712байтами. Если вы отбросите свой индекс и попробуете вставить снова, он должен работать для вас. Если вам нужно проиндексировать большее поле, вы можете изучить возможности полнотекстовой индексации postgres.

jcern
источник
2

Я получил это в колонке географии PostGIS. Это потому, что я случайно создал индекс неправильно. Вы должны включить параметр USING GIST при создании таких индексов.

Брэд Мэтьюз
источник
Спасибо - это было это! Ух, пока что надуманно. Возможно, сэкономил мне часы. Тем более, что я думал, что GiST используется по умолчанию, но я ошибся, и он пытается использовать b-tree.
Джонас