Som du har det måste likheten mellan varje element och alla andra element i tabellen beräknas (nästan en korsfogning). Om din tabell har 1 000 rader, är det redan 1 000 000 (!) likhetsberäkningar, före dessa kan kontrolleras mot skicket och sorteras. Skalar fruktansvärt.
Använd SET pg_trgm.similarity_threshold
och %
operatör istället. Båda tillhandahålls av pg_trgm
modul. På så sätt kan ett trigram GiST-index användas med stor effekt.
Konfigurationsparametern pg_trgm.similarity_threshold
ersatte funktionerna set_limit()
och show_limit()
i Postgres 9.6. De föråldrade funktionerna fungerar fortfarande (från och med Postgres 13). Dessutom har prestanda för GIN- och GiST-index förbättrats på många sätt sedan Postgres 9.1.
Försök istället:
SET pg_trgm.similarity_threshold = 0.8; -- Postgres 9.6 or later
SELECT similarity(n1.name, n2.name) AS sim, n1.name, n2.name
FROM names n1
JOIN names n2 ON n1.name <> n2.name
AND n1.name % n2.name
ORDER BY sim DESC;
Snabbare i storleksordningar, men fortfarande långsamt.
pg_trgm.similarity_threshold
är ett "anpassat" alternativ, som kan hanteras som alla andra alternativ. Se:
- Fråga en parameter (postgresql.conf-inställning) som "max_connections"
Du kanske vill begränsa antalet möjliga par genom att lägga till förutsättningar (som att matcha första bokstäver) före korsfogning (och stödja det med ett matchande funktionsindex). Prestanda för en korskoppling försämras med O(N²) .
Detta fungerar inte eftersom du inte kan referera till utdatakolumner i WHERE
eller HAVING
klausuler:
WHERE ... sim > 0.8
Det är enligt SQL-standarden (som hanteras ganska löst av vissa andra RDBMS). Å andra sidan:
ORDER BY sim DESC
Fungerar eftersom utdatakolumner kan användas i GROUP BY
och ORDER BY
. Se:
- PostgreSQL återanvändning av beräkningsresultat i urvalsfråga
Testfall
Jag körde ett snabbtest på min gamla testserver för att verifiera mina påståenden.
PostgreSQL 9.1.4. Tider tagna med EXPLAIN ANALYZE
(bäst av 5).
CREATE TEMP table t AS
SELECT some_col AS name FROM some_table LIMIT 1000; -- real life test strings
Första omgången av tester med GIN-index:
CREATE INDEX t_gin ON t USING gin(name gin_trgm_ops); -- round1: with GIN index
Andra omgången av tester med GIST-index:
DROP INDEX t_gin;
CREATE INDEX t_gist ON t USING gist(name gist_trgm_ops);
Ny fråga:
SELECT set_limit(0.8);
SELECT similarity(n1.name, n2.name) AS sim, n1.name, n2.name
FROM t n1
JOIN t n2 ON n1.name <> n2.name
AND n1.name % n2.name
ORDER BY sim DESC;
GIN-index används, 64 träffar:total körtid:484,022 ms
GIST-index används, 64 träffar:total körtid:248,772 ms
Gammal fråga:
SELECT (similarity(n1.name, n2.name)) as sim, n1.name, n2.name
FROM t n1, t n2
WHERE n1.name != n2.name
AND similarity(n1.name, n2.name) > 0.8
ORDER BY sim DESC;
GIN-index inte använd, 64 träffar:total körtid:6345,833 ms
GIST-index inte använd, 64 träffar:total körtid:6335,975 ms
I övrigt identiska resultat. Råd är bra. Och det här är för bara 1 000 rader !
GIN eller GiST?
GIN ger ofta överlägsen läsprestanda:
- Skillnad mellan GiST och GIN-index
Men inte i det här fallet!
Detta kan implementeras ganska effektivt av GiST-index, men inte av GIN-index.
- Flerkolumnindex på 3 fält med heterogena datatyper