| From: | Thomas Uzunoff <postgresql(at)grizzlycrm(dot)de> | 
|---|---|
| To: | pgsql-de-allgemein(at)postgresql(dot)org | 
| Subject: | Suche doppelter Datensätze zu langsam | 
| Date: | 2012-11-13 08:25:54 | 
| Message-ID: | 50A20412.2050001@grizzlycrm.de | 
| Views: | Whole Thread | Raw Message | Download mbox | Resend email | 
| Thread: | |
| Lists: | pgsql-de-allgemein | 
Hallo,
ich möchte in einer Tabelle doppelte Datensätze aufspüren. Hierzu 
benutze ich SOUNDEX() zur phonetischen Suche. Das Problem ist, dass die 
bisherige Version der Prüfung bei den Kriterien name und strasse nicht 
alle beabsichtigten Daten findet, nämlich aus der folgenden Tabelle nur 
die ersten beiden (BMW) Datensätze. Es sollen aber zum Beispiel auch die 
Datensätze in den Zeilen 3 und 4 gefunden werden.
id       name            strasse
----------------------------------------------------
1        BMW            Hauptstrasse 55
2        BMW            Hauptstrasse 57
3        Siemens       Hochweg 40
4        Siemens
5        Post              Bonner Strasse 90
Dazu habe ich die letzten beiden Zeilen der Abfrage mit den ORs 
erweitert. Logisch scheint auch das richtige Ergebnis rauszukommen, nur 
ist es jetzt sehr sehr langsam und dass bei einer Menge von gerade mal 
10.000 Datensätzen. Die LENGTH Funktion dient dazu herauszufinden, ob an 
die SOUNDEX() etwas Auswertbares übergeben wurde und falls das nicht der 
Fall ist (also der SOUNDEX-Wert nicht 4 Zeichen lang ist), der Datensatz 
trotzdem als Dublette gefunden werden kann.
SELECT DISTINCT
       company1.company__id
     , company1.company__name
     , company1.company__streetno
FROM
       company company1
     , company company2
WHERE
          company1.company__id<>company2.company__id
     AND 
(SOUNDEX(company1.company__name)=SOUNDEX(company2.company__name) OR 
LENGTH(SOUNDEX(company1.company__name))=0 OR 
LENGTH(SOUNDEX(company2.company__name))=0)
     AND 
(SOUNDEX(company1.company__streetno)=SOUNDEX(company2.company__streetno) 
OR LENGTH(SOUNDEX(company1.company__streetno))=0 OR 
LENGTH(SOUNDEX(company2.company__streetno))=0)
Ich habe mir daraufhin die Ausführungspläne angesehen. Der 
Ausführungsplan für obige Abfrage ist der folgende:
"HashAggregate  (cost=4481104.91..4481184.91 rows=8000 width=57)"
"  ->  Nested Loop  (cost=0.00..4480998.00 rows=14255 width=57)"
"        Join Filter: ((company1.company__id <> company2.company__id) 
AND ((soundex(company1.company__streetno) = 
soundex(company2.company__streetno)) OR 
(length(soundex(company1.company__streetno)) = 0) OR 
(length(soundex(company2.company__streetno)) = 0)) AND 
((soundex((company1.company__name)::text) = 
soundex((company2.company__name)::text)) OR 
(length(soundex((company1.company__name)::text)) = 0) OR 
(length(soundex((company2.company__name)::text)) = 0)))"
"        ->  Seq Scan on company company1  (cost=0.00..489.00 rows=8000 
width=57)"
"        ->  Materialize  (cost=0.00..529.00 rows=8000 width=57)"
"              ->  Seq Scan on company company2 (cost=0.00..489.00 
rows=8000 width=57)"
Wenn ich die ursprüngliche Abfrage ausführe, die so aussieht:
SELECT DISTINCT
       company1.company__id
     , company1.company__name
     , company1.company__streetno
FROM
       company company1
     , company company2
WHERE
          company1.company__id<>company2.company__id
     AND (SOUNDEX(company1.company__name)=SOUNDEX(company2.company__name) )
     AND 
(SOUNDEX(company1.company__streetno)=SOUNDEX(company2.company__streetno))
Ist der Ausführungsplan dieser:
"HashAggregate  (cost=2407.26..2423.26 rows=1600 width=57)"
"  ->  Merge Join  (cost=2015.26..2395.26 rows=1600 width=57)"
"        Merge Cond: (((soundex((company1.company__name)::text)) = 
(soundex((company2.company__name)::text))) AND 
((soundex(company1.company__streetno)) = 
(soundex(company2.company__streetno))))"
"        Join Filter: (company1.company__id <> company2.company__id)"
"        ->  Sort  (cost=1007.63..1027.63 rows=8000 width=57)"
"              Sort Key: (soundex((company1.company__name)::text)), 
(soundex(company1.company__streetno))"
"              ->  Seq Scan on company company1 (cost=0.00..489.00 
rows=8000 width=57)"
"        ->  Sort  (cost=1007.63..1027.63 rows=8000 width=57)"
"              Sort Key: (soundex((company2.company__name)::text)), 
(soundex(company2.company__streetno))"
"              ->  Seq Scan on company company2 (cost=0.00..489.00 
rows=8000 width=57)"
Ich habe den Eindruck, dass das Nested Loop mit der Verlangsamung etwas 
zu tun hat, habe aber noch keinen Weg gefunden, postgres beizubringen, 
den Nested Loop hier nicht zu verwenden.
Ich habe schon versucht, mit funktionionellen Indizes auf beide Felder 
SOUNDEX(company__name) und SOUNDEX(company__streetno) die Suche zu 
beschleunigen - hat aber auch nicht geholfen. Über eine Idee, wie ich 
die obere Abfrage so beschleunigen kann, dass sie ähnlich schnell oder 
noch besser als die untere Abfrage läuft, wäre ich sehr dankbar.
Danke & Grüße
Thomas
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Christopher Westerfield | 2012-11-20 16:05:11 | Frage zum aufsetzen eines multi-server gespanns | 
| Previous Message | Wolfgang Keller | 2012-11-10 13:22:25 | Re: Werkzeug für Datenbankanwendung gesucht |