2009-05-05 3 views
1

У меня есть требование создать список возможных дубликатов, прежде чем пользователь сохранит объект в базе данных и предупредит их о возможных дубликатах.Эффективная проверка возможных повторяющихся объектов

Есть 7 критериев, по которым мы должны проверять дубликаты и если не менее 3 совпадений, мы должны отметить это до пользователя. Критерии будут совпадать по идентификатору, поэтому вам не понадобится сопряжение с нечеткой строкой, но моя проблема возникает из-за того, что существует множество возможных способов (99 способов, если я сделал свои суммы corerctly), по крайней мере, для 3 элементов, которые соответствуют список из 7 возможных.

Я не хочу делать 99 отдельных запросов на поиск, чтобы найти результаты поиска, и я также не хочу вернуть всю партию обратно из db и фильтровать на стороне клиента. Мы, вероятно, сейчас говорим о нескольких десятках тысяч записей в настоящее время, но это будет расти в миллионы по мере созревания системы.

Кто-нибудь получил хоть какой-нибудь хороший эффективный способ сделать это? Я рассматривал простой запрос OR, чтобы получить записи, где по крайней мере одно поле соответствует из db, а затем выполняет некоторую обработку на клиенте, чтобы отфильтровать его еще несколько, но некоторые из полей имеют очень низкую мощность и на самом деле не будут уменьшить количество на огромную сумму.

Благодаря Джон

ответ

3

OR и CASE суммирующий будет работать, но весьма неэффективно, так как они не используют индексы.

Вам необходимо сделать UNION для удобства использования индексов.

Если пользователь вводит name, phone, email и address в базу данных, и вы хотите, чтобы проверить все записи, которые соответствуют по крайней мере 3 этих полей, вы выдаете:

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 

Это будет использовать индексы эти поля и запрос будут быстрыми.

Смотрите эту статью в своем блоге подробности:

  • Matching 3 of 4: как соответствовать запись, которая соответствует по меньшей мере 3 из 4 возможных условий

Также смотрите question статья основана на ,

Если вы хотите иметь список DISTINCT значений в существующих данных, вы просто обернуть этот запрос в подзапрос:

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 
     ) 

Обратите внимание, что это DISTINCT не транзитивно: если A матчи B и B матчи C, это не означает, что A соответствует C.

+0

Спасибо, подумайте, что это похоже на лучшее решение для моей проблемы. Нам по-прежнему придется делать группу на множестве с множеством элементов, но выполнение небольшого тестирования кажется более быстрым, чем другие способы, которые я пробовал. – JonC

0

Какие DBS вы используете? Некоторая поддержка с использованием таких ограничений с использованием кода на стороне сервера.

0

Рассматривали ли вы использование хранимой процедуры с помощью курсора? Затем вы можете выполнить свой запрос OR, а затем выполнить записи по одному для поиска матчей.Использование хранимой процедуры позволит вам выполнить все проверки на сервере.

Однако, я думаю, что сканирование таблицы с миллионами записей всегда будет медленным. Я думаю, вы должны решить, какой из 7 полей, скорее всего, будет соответствовать, убедитесь, что они проиндексированы.

0

Я предполагаю, что ваша система пытается сопоставить идентификаторы тегов определенного сообщения или что-то подобное. Это отношение multi-to-multi, и вы должны иметь три таблицы для его обработки. Один для сообщения, один для тегов и один для сообщений и тэгов.

Если мои предположения верны, то лучший способ справиться с этим:

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

Вы могли бы хотеть что-то вроде следующего:

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 

Это не тестировался, но он показывает способ справиться с этим.