Проблема
Ось дуже подібний випадок, обговорений на 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
посилається на a 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
, яке постачає додаткові оператори та класи операторів.
data
містить список тегів, як це демонстрував у цій пов’язаній публікації блогу Скотт Снайдер ? Якщо це так, я можу мати краще рішення для вас.