sql >> Databasteknik >  >> RDS >> PostgreSQL

Hitta liknande strängar med PostgreSQL snabbt

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



  1. Postgres COUNT antal kolumnvärden med INNER JOIN

  2. Hur man väljer * men utan kolumnnamn måste vara unika i varje vy

  3. Installera Percona XtraDB Cluster på CentOS 7

  4. Hur clock_timestamp() fungerar i PostgreSQL