Поиск дубликатов записей в MySQL на основе битовой маски - PullRequest
2 голосов
/ 02 августа 2010

У меня есть таблица mysql, в которой хранятся журналы технического обслуживания датчиков.Я хотел бы разработать запрос, который находит случаи, когда данный датчик был отремонтирован / обслуживается по той же причине.(Средство поиска повторяющихся проблем.)

Моя таблица (упрощенная) выглядит следующим образом:

id    name     mask
==    ====     ====
11    alpha    0011
12    alpha    0010
13    alpha    0100
14    beta     0001

Поле маски представляет собой битовую маску, в которой каждая позиция представляет определенный тип восстановления.Мне удалось успешно выяснить, как сравнить битовую маску ( для этого вопроса ), но попытка включить ее в запрос оказывается более сложной, чем я думал.

Учитывая приведенные выше примеры записейприменяются только идентификаторы 11 и 12, так как они оба имеют 1 в третьей позиции маски.

Вот что я пробовал и почему это не сработало:


1.Никогда не заканчивается ...
Этот запрос, кажется, выполняется вечно, я не думаю, что он работает так, как я хочу.
SELECT t1.id, t1.name
FROM data t1
LEFT OUTER JOIN data t2
ON (CONV(t1.mask,2,10) & CONV(t2.mask,2,10) > 0)
GROUP BY t1.name
HAVING COUNT(*) >1;

2,Неполный запрос ...
Я думал о создании представления, чтобы анализировать только те датчики, которые на самом деле имеют более одной записи в таблице.Я не был уверен, куда идти.

SELECT COUNT(t1.name) AS times, t1.name, t1.id, t1.mask
FROM data AS t1
GROUP BY t1.name ASC
HAVING times > 1;

Есть предложения по этому поводу?

Ответы [ 3 ]

2 голосов
/ 04 августа 2010

Поскольку структура базы данных не была разработана с учетом реалий RDBM (вероятно, это не ваше дело, я просто должен все равно указать на это ...), производительность всегда будет низкой, хотя можно написать запрос, который закончу.

Джим прав в том, что запрос приводит к декартовому произведению. Если этот запрос будет возвращен без группировки и без фильтрации, вы можете ожидать (SELECT POW(COUNT(*), 2) FROM data) результатов. Кроме того, любая форма внешнего объединения не является необходимой, поэтому здесь вам нужно стандартное внутреннее объединение (не то, чтобы это могло иметь значение с точки зрения производительности, это просто более уместно).

Также необходимо другое условие объединения, t1.id != t2.id, чтобы каждая запись не соответствовала самой себе.

SELECT t1.id, t1.name
FROM data t1
JOIN data t2
     ON t1.name = t2.name
     AND t1.id != t2.id     //
WHERE CONV(t1.mask, 2, 10) & CONV(t2.mask, 2, 10) > 0
GROUP BY t1.name
HAVING COUNT(*) > 1;

Ваш неполный запрос:

SELECT t1.id, t1.name, t1.mask
FROM data t1
WHERE t1.name IN (SELECT t2.name FROM data t2 GROUP BY t2.name HAVING COUNT(*) > 1);

SELECT t1.id, t1.name, t1.mask
FROM data t1
WHERE EXISTS (SELECT 1 FROM data t2 WHERE t2.name = t1.name GROUP BY t2.name HAVING COUNT(*) > 1);

От головы до головы, я не могу сказать вам, какой из них будет лучше. Если data.name проиндексирован (и я надеюсь, что это так), то стоимость любого запроса должна быть довольно низкой. Первый будет кэшировать копию подвыбора, тогда как второй будет выполнять несколько запросов к индексу.

Одной из самых основных оптимизаций (при условии, что структура таблицы остается неизменной) будет преобразование поля маски в тип данных целых чисел без знака, что позволит сохранить множество вызовов в CONV().

WHERE CONV(t1.mask, 2, 10) & CONV(t2.mask, 2, 10) > 0

становится

WHERE t1.mask & t2.mask > 0

Конечно, разбивать данные дальше имеет смысл. Вместо того, чтобы хранить битовую маску в одной записи, разбейте все эти биты на отдельные записи

id    name     mask
==    ====     ====
11    alpha    1101

станет

id    name    value
==    ====    =====
11    alpha       1
12    alpha       4
13    alpha       8

Теперь стратегически размещенный индекс на name и value превращает запрос в кусок пирога

SELECT name, value
FROM data
GROUP BY name, value
HAVING COUNT(*) > 1;

Надеюсь, это поможет.

0 голосов
/ 02 августа 2010

Ваше объединение приводит к декартовому произведению таблицы с самим собой. Добавьте в объединение `t1.name = t2.name ', дав кучу (намного) меньших декартовых произведений, по одному на уникальное имя, что значительно ускорит процесс.

0 голосов
/ 02 августа 2010

Разбить биты маски в реальных столбцах.RDMB не любят битовые поля.

...