2009-05-05 8 views
1

J'ai l'obligation de produire une liste de doublons possibles avant qu'un utilisateur enregistre une entité dans la base de données et les avertisse des doublons possibles.Vérification efficace des entités dupliquées possibles

Il y a 7 critères sur lesquels nous devrions vérifier les doublons et si au moins 3 correspond, nous devrions signaler cela à l'utilisateur. Les critères vont tous correspondre sur l'ID, donc il n'y a pas de correspondance de chaîne floue nécessaire mais mon problème vient du fait qu'il y a plusieurs façons possibles (99 façons si j'ai fait mes sommes) de faire correspondre au moins 3 éléments la liste des 7 possibles. Je ne veux pas avoir à faire 99 requêtes db séparées pour trouver mes résultats de recherche et je ne veux pas non plus ramener tout le lot de la base de données et du filtre côté client. Nous ne parlons probablement que de quelques dizaines de milliers d'enregistrements à l'heure actuelle, mais cela atteindra des millions à mesure que le système évoluera.

Quelqu'un a-t-il un moyen efficace de le faire? Je considérais une simple requête OU pour obtenir les enregistrements où au moins un champ correspond à la base de données et ensuite effectuer un traitement sur le client pour le filtrer un peu plus, mais quelques champs ont une cardinalité très faible et ne seront pas réellement réduire les chiffres d'une énorme quantité.

Merci Jon

Répondre

3

OR et CASE sommateur fonctionnera, mais sont tout à fait inefficaces, car ils ne pas utiliser les index.

Vous devez définir UNION pour que les index soient utilisables.

Si un utilisateur entre name, phone, email et address dans la base de données, et que vous voulez vérifier tous les enregistrements qui correspondent à au moins 3 de ces domaines, vous émettez:

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 

Cela utilisera des index sur ces champs et la requête seront rapides.

Voir cet article dans mon blog pour plus de détails:

  • Matching 3 of 4: comment faire correspondre un enregistrement qui correspond à au moins 3 de 4 conditions

Voir aussi ce question l'article est basé sur .

Si vous voulez avoir une liste des DISTINCT valeurs dans les données existantes, vous conclure cette requête dans une sous-requête:

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 
     ) 

Notez que ce DISTINCT est pas transitive: si A matchs B et B matchs C, cela ne signifie pas que A correspond à C.

+0

Merci, pense que celui-ci ressemble à la meilleure solution pour mon problème. Nous allons encore devoir faire un groupe sur un ensemble avec beaucoup d'éléments, mais faire un peu de test semble être plus rapide que les autres méthodes que j'ai essayées. – JonC

0

Quels DBS utilisez-vous? Certains prennent en charge l'utilisation de ces contraintes en utilisant le code côté serveur.

0

Avez-vous envisagé d'utiliser une procédure stockée avec un curseur? Vous pouvez ensuite faire votre requête OR et ensuite parcourir les dossiers un par un à la recherche de correspondances.L'utilisation d'une procédure stockée vous permettrait de faire toutes les vérifications sur le serveur. Cependant, je pense qu'un balayage de table avec des millions d'enregistrements sera toujours lent. Je pense que vous devriez déterminer lesquels des 7 champs sont les plus susceptibles de correspondre, assurez-vous qu'ils sont indexés.

0

Je suppose que votre système essaie de faire correspondre les identifiants d'étiquette d'un certain article, ou quelque chose de similaire. C'est une relation multi-multi-et vous devriez avoir trois tables pour le gérer. Un pour le poste, un pour les tags et un pour les relations post et tags.

Si mes hypothèses sont correctes alors la meilleure façon de gérer cela est:

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

Vous voudrez peut-être quelque chose comme ce qui suit:

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 

Ce n'est pas testé, mais il montre un façon d'aborder cela.

Questions connexes