Das Problem
Hier ist ein sehr ähnlicher Fall, der auf pgsql.general diskutiert wird . Es geht um die Einschränkung in einem B-Tree-Index, aber es ist alles das Gleiche, da ein GIN-Index intern einen B-Tree-Index für Schlüssel verwendet und daher auf dieselbe Einschränkung für die Schlüsselgröße stößt (anstelle der Elementgröße in einem einfachen B-Tree) Index).
Ich zitiere das Handbuch zur Implementierung des GIN-Index :
Intern enthält ein GIN-Index einen B-Tree-Index, der über Schlüsseln erstellt wird, wobei jeder Schlüssel ein Element eines oder mehrerer indizierter Elemente ist
In beiden Fällen ist mindestens ein Array-Element in Ihrer Spalte data
zu groß, um indiziert zu werden. Wenn dies nur ein einzelner Freak-Wert oder eine Art Unfall ist, können Sie den Wert möglicherweise abschneiden und damit fertig werden.
Für die folgende Demo gehe ich anders aus: viele Langtextwerte im Array.
Einfache Lösung
Sie können Elemente in Ihrem Array data
durch entsprechende Hashwerte ersetzen . Und senden Sie Suchwerte über dieselbe Hash-Funktion. Natürlich möchten Sie Ihre Originale wahrscheinlich zusätzlich irgendwo aufbewahren. Damit kommen wir fast zu meiner zweiten Variante ...
Erweiterte Lösung
Sie können eine Nachschlagetabelle für Array-Elemente mit einer serial
Spalte als Ersatz-Primärschlüssel erstellen (praktisch eine radikale Art von Hash-Wert). Dies ist umso interessanter, wenn die beteiligten Elementwerte nicht eindeutig sind:
CREATE TABLE elem (
elem_id serial NOT NULL PRIMARY KEY
, elem text UNIQUE NOT NULL
);
Da wir nachschlagen möchten elem
, fügen wir einen Index hinzu - diesmal jedoch einen Index für einen Ausdruck mit nur den ersten 10 Zeichen des Langtextes. Dies sollte in den meisten Fällen ausreichen, um eine Suche auf einen oder mehrere Treffer zu beschränken. Passen Sie die Größe an Ihre Datenverteilung an. Oder verwenden Sie eine komplexere Hash-Funktion.
CREATE INDEX elem_elem_left10_idx ON elem(left(elem,10));
Ihre Spalte data
wäre dann vom Typ int[]
. Ich habe den Tisch in umbenannt data
und das Unheil, das varchar(50)
Sie in Ihrem Beispiel hatten, beseitigt:
CREATE TEMP TABLE data(
data_id serial PRIMARY KEY
, data int[]
);
Jedes Array-Element in data
bezieht sich auf a elem.elem_id
. An diesem Punkt können Sie in Betracht ziehen, die Array-Spalte durch eine n: m-Tabelle zu ersetzen, wodurch Ihr Schema normalisiert wird und Postgres die erzwungene Integrität erzwingen kann. Indizierung und allgemeine Handhabung werden einfacher ...
Aus Leistungsgründen kann die int[]
Spalte in Kombination mit einem GIN-Index jedoch überlegen sein. Die Speichergröße ist viel kleiner. In diesem Fall benötigen wir den GIN-Index:
CREATE INDEX data_data_gin_idx ON data USING GIN (data);
Jetzt ist jeder Schlüssel des GIN-Index (= Array-Element) ein integer
statt eines länglichen text
. Der Index wird um mehrere Größenordnungen kleiner sein, die Suche wird folglich viel schneller sein.
Der Nachteil: Bevor Sie tatsächlich eine Suche durchführen können, müssen Sie die elem_id
von der Tabelle nachschlagen elem
. Mit meinem neu eingeführten Funktionsindex elem_elem_left10_idx
wird auch dies viel schneller sein.
Sie können alles in einer einfachen Abfrage erledigen :
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
Möglicherweise interessiert Sie die Erweiterung intarray
, die zusätzliche Operatoren und Operatorklassen bereitstellt.
data
eine Liste von Tags enthält, wie in diesem verwandten Blog-Beitrag von Scott Snyder gezeigt ? In diesem Fall habe ich möglicherweise eine bessere Lösung für Sie.