Verwenden Sie für Ihre Art der Mustererkennung am besten einen Trigrammindex. Lesen Sie dies zuerst:
Ich gehe davon aus, dass Ihr Ausdruck einen Tippfehler enthält (first_name || '' || last_name)
, der bei einer leeren Zeichenfolge keinen Sinn ergibt, und Sie möchten wirklich (first_name || ' ' || last_name)
- mit einem Leerzeichen.
Unter der Annahme, dass jede Spalte NULL sein kann, benötigen Sie eine NULL-sichere Verkettung. Die einfache Lösung lautet concat_ws()
:
Diese Funktion ist jedoch nicht IMMUTABLE
(Erklärung in der verknüpften Antwort), sodass Sie sie nicht direkt in einem Indexausdruck verwenden können. Sie könnten einen IMMUTABLE
Funktions-Wrapper verwenden:
CREATE OR REPLACE FUNCTION f_immutable_concat_ws(s text, t1 text, t2 text)
RETURNS text AS
$func$
SELECT concat_ws(s, t1, t2)
$func$ LANGUAGE sql IMMUTABLE;
Der Wrapper kann sein, IMMUTABLE
weil er nur text
Parameter akzeptiert.
In beiden Fällen ist dies ausführlicher, hat jedoch weniger internen Aufwand und ist erheblich schneller:
CREATE OR REPLACE FUNCTION f_immutable_concat_ws1(s text, t1 text, t2 text)
RETURNS text AS
$func$
SELECT CASE
WHEN t1 IS NULL THEN t2
WHEN t2 IS NULL THEN t1
ELSE t1 || s || t2
END
$func$ LANGUAGE sql IMMUTABLE;
Oder mit fest codiertem Leerzeichen:
CREATE OR REPLACE FUNCTION f_concat_space(t1 text, t2 text)
RETURNS text AS
$func$
SELECT CASE
WHEN t1 IS NULL THEN t2
WHEN t2 IS NULL THEN t1
ELSE t1 || ' ' || t2
END
$func$ LANGUAGE sql IMMUTABLE;
Basierend auf dieser Funktion schlage ich vor:
CREATE INDEX people_special_gin_trgm_idx ON people
USING gin (f_concat_space(first_name, last_name) gin_trgm_ops, email gin_trgm_ops);
Ich habe email
als zweite Indexspalte für mehrere Überlegungen hinzugefügt .
Das Erstellen des Index dauert eine Weile für 20 Millionen Zeilen, am besten nicht während des Topladens oder möglicherweise bei Verwendung CREATE INDEX CONCURRENTLY ...
. Ein GIN-Index ist erheblich größer als ein einfacher Btree-Index und in der Wartung auch teurer. Stellen Sie sicher, dass Sie die neueste Version von Postgres ausführen. In den letzten Versionen wurden die GIN-Indizes erheblich verbessert.
Dann sollte Ihre leicht angepasste und vereinfachte Abfrage schnell und korrekt sein :
SELECT *
FROM people
WHERE f_concat_space(first_name, last_name) ILIKE '%bat%man%' OR
email ILIKE '%bat%man%'
LIMIT 25;
Sie benötigen nur den einen Index für diese Abfrage.
Grundlagen für den Mustervergleich: