2009-05-05 16 views
1

Ich habe eine Anforderung, eine Liste möglicher Duplikate zu erstellen, bevor ein Benutzer eine Entität in der Datenbank speichert und sie vor den möglichen Duplikaten warnt.Effiziente Prüfung möglicher doppelter Entitäten

Es gibt 7 Kriterien, nach denen wir die Duplikate überprüfen sollten und wenn mindestens 3 übereinstimmen, sollten wir dies dem Benutzer melden. Die Kriterien stimmen alle auf ID überein, so dass kein Fuzzy-String-Matching benötigt wird, aber mein Problem rührt von der Tatsache her, dass es viele Möglichkeiten gibt (99 Wege, wenn ich meine Summen korrekt gemacht habe) für mindestens 3 Items die Liste von 7 möglichen.

Ich will nicht 99 separate DB-Abfragen zu finden, um meine Suchergebnisse zu finden, und ich möchte auch nicht die ganze Menge zurück von der db und Filter auf der Client-Seite. Wir sprechen derzeit wahrscheinlich nur von ein paar Zehntausenden von Datensätzen, aber dies wird mit der Reifung des Systems in die Millionen wachsen.

Jeder hat eine gute Möglichkeit, dies zu tun? Ich erwog eine einfache OR-Abfrage, um die Datensätze zu erhalten, wo mindestens ein Feld von der db übereinstimmt und dann eine Verarbeitung auf dem Client zu tun, um sie etwas mehr zu filtern, aber einige der Felder haben eine sehr geringe Kardinalität und werden nicht wirklich Reduziere die Anzahl um einen großen Betrag.

Dank Jon

Antwort

3

OR und CASE Summierung funktionieren, sind aber ziemlich ineffizient, da sie keine Indizes verwenden.

Sie müssen UNION machen, damit Indizes verwendbar sind.

Wenn ein Benutzer name eintritt, phone, email und address in die Datenbank, und Sie möchten alle Datensätze überprüfen, die zumindest 3 dieser Felder entsprechen, geben Sie:

SELECT i.* 
FROM (
     SELECT id, COUNT(*) 
     FROM (
       SELECT id 
       FROM t_info t 
       WHERE name = 'Eve Chianese' 
       UNION ALL 
       SELECT id 
       FROM t_info t 
       WHERE phone = '+15558000042' 
       UNION ALL 
       SELECT id 
       FROM t_info t 
       WHERE email = '[email protected]' 
       UNION ALL 
       SELECT id 
       FROM t_info t 
       WHERE address = '42 North Lane' 
       ) q 
     GROUP BY 
       id 
     HAVING COUNT(*) >= 3 
     ) dq 
JOIN t_info i 
ON  i.id = dq.id 

Dies Indizes verwenden, auf Diese Felder und die Abfrage werden schnell sein.

diesen Artikel in meinem Blog Siehe Details:

  • Matching 3 of 4: wie einen Datensatz entsprechen, die mindestens 3 von 4 möglichen Bedingungen entspricht

dieses question wird siehe auch den Artikel basiert auf .

Wenn Sie eine Liste von DISTINCT Werte in den vorhandenen Daten haben wollen, wickeln Sie ihn nur Abfrage in einer Unterabfrage:

SELECT i.* 
FROM t_info i1 
WHERE EXISTS 
     (
     SELECT 1 
     FROM (
       SELECT id 
       FROM t_info t 
       WHERE name = i1.name 
       UNION ALL 
       SELECT id 
       FROM t_info t 
       WHERE phone = i1.phone 
       UNION ALL 
       SELECT id 
       FROM t_info t 
       WHERE email = i1.email 
       UNION ALL 
       SELECT id 
       FROM t_info t 
       WHERE address = i1.address 
       ) q 
     GROUP BY 
       id 
     HAVING COUNT(*) >= 3 
     ) 

Beachten Sie, dass diese DISTINCT nicht transitiv ist: Wenn A Streichhölzer B und B Streichhölzer C bedeutet dies nicht, dass A mit C übereinstimmt.

+0

Danke, denke, dass diese die beste Lösung für mein Problem aussieht. Wir werden immer noch eine Gruppe mit mehreren Elementen am Set machen müssen, aber ein bisschen testen scheint das schneller zu sein als andere Möglichkeiten, die ich ausprobiert habe. – JonC

0

Welche DBS verwenden Sie? Einige unterstützen die Verwendung solcher Einschränkungen durch Verwendung von serverseitigem Code.

0

Haben Sie überlegt, eine gespeicherte Prozedur mit einem Cursor zu verwenden? Sie könnten dann Ihre OR-Abfrage durchführen und dann nacheinander die Datensätze durchsuchen und nach Übereinstimmungen suchen.Mit einer gespeicherten Prozedur können Sie alle Prüfungen auf dem Server durchführen.

Allerdings denke ich, dass ein Tabellenscan mit Millionen von Datensätzen immer langsam sein wird. Ich denke, Sie sollten herausfinden, welche der 7 Felder am ehesten übereinstimmen, stellen Sie sicher, dass diese indexiert sind.

0

Ich gehe davon aus, dass Ihr System versucht, Tag IDs eines bestimmten Beitrags oder etwas ähnliches zu entsprechen. Dies ist eine Multi-zu-Multi-Beziehung und Sie sollten drei Tabellen haben, um damit umzugehen. Eine für die Post, eine für Tags und eine für die Beziehung Post und Tags.

Wenn meine Annahmen korrekt sind dann der beste Weg, dies zu handhaben ist:

SELECT postid, count(tagid) as common_tag_count 
FROM posts_to_tags 
WHERE tagid IN (tag1, tag2, tag3, ...) 
GROUP BY postid 
HAVING count(tagid) > 3; 
2

Sie etwas wollen, könnte wie folgt aus:

SELECT id 
FROM 
    (select id, CASE fld1 WHEN input1 THEN 1 ELSE 0 "rule1", 
     CASE fld2 when input2 THEN 1 ELSE 0 "rule2", 
     ..., 
     CASE fld7 when input7 THEN 1 ELSE 0 "rule2", 
    FROM table) 
WHERE rule1+rule2+rule3+...+rule4 >= 3 

Dies ist nicht getestet, aber es zeigt ein Weg, dies anzugehen.