2016-10-11 2 views
0

ОписаниеЭффективный способ для вычисления количества паросочетаний между двумя столбцами в MySQL

У меня есть таблица MySQL, как следующему:

CREATE TABLE `ticket` (
    `ticket_id` int(11) NOT NULL AUTO_INCREMENT, 
    `ticket_number` varchar(30) DEFAULT NULL, 
    `pick1` varchar(2) DEFAULT NULL, 
    `pick2` varchar(2) DEFAULT NULL, 
    `pick3` varchar(2) DEFAULT NULL, 
    `pick4` varchar(2) DEFAULT NULL, 
    `pick5` varchar(2) DEFAULT NULL, 
    `pick6` varchar(2) DEFAULT NULL, 
    PRIMARY KEY (`ticket_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=19675 DEFAULT CHARSET=latin1; 

Давайте также предполагаю, мы имеем следующие значения уже сохранены в DB:

+-----------+-------------------+-------+-------+-------+-------+-------+-------+ 
| ticket_id | ticket_number | pick1 | pick2 | pick3 | pick4 | pick5 | pick6 | 
+-----------+-------------------+-------+-------+-------+-------+-------+-------+ 
| 655  | 08-09-21-24-46-52 | 8  | 9  | 21 | 24 | 46 | 52 | 
| 658  | 08-23-24-40-42-45 | 8  | 23 | 24 | 40 | 42 | 45 | 
| 660  | 07-18-19-20-22-31 | 7  | 18 | 19 | 20 | 22 | 45 | 
| ...  | ...    | ... | ... | ... | ... | ... | ... | 
| 19674  | 06-18-33-43-49-50 | 6  | 18 | 33 | 43 | 49 | 50 | 
+-----------+-------------------+-------+-------+-------+-------+-------+-------+ 

Теперь моя цель состоит в том, чтобы сравнить каждый билет с каждой другой в таблице (кроме себя), с точки зрения thei r соответствующие значения в поле ticket_number (6 элементов в наборе, разделены на -). Иными словами, предположим, что я сравниваю ticket_id = 655 с ticket_id = 658, с точки зрения элементов в их уважаемых полях ticket_number, то обнаружим, что в обоих наборах появляются элементы 08 и 24. Если сравнить ticket_id = 660 с ticket_id = 19674, то у нас есть только одно совпадение: 18.

То, что я на самом деле, используя для проведения этих сравнений является следующий запрос:

select A.ticket_id, A.ticket_number, P.ticket_id, P.ticket_number, count(P.ticket_number) as cnt from ticket A inner join ticket P on A.ticket_id != P.ticket_id 
where 
((A.ticket_number like concat("%", lpad(P.pick1,2,0), "%")) 
+ (A.ticket_number like concat("%", lpad(P.pick2,2,0), "%")) 
+ (A.ticket_number like concat("%", lpad(P.pick3,2,0), "%")) 
+ (A.ticket_number like concat("%", lpad(P.pick4,2,0), "%")) 
+ (A.ticket_number like concat("%", lpad(P.pick5,2,0), "%")) 
+ (A.ticket_number like concat("%", lpad(P.pick6,2,0), "%")) > 3) group by A.ticket_id 
having cnt > 5; 

То есть, первый я создаю INNER JOIN конкатенации всех строк с различными ticket_id, а затем я сравниваю каждый P.pickX (X=[1..6]) с A.ticket_number итоговой операции INNER JOIN, и я подсчитываю количество совпадений между обоими наборами.

Наконец, после выполнения, я получаю что-то вроде этого:

+-------------+-------------------+-------------+-------------------+-----+ 
| A.ticket_id | A.ticket_number | P.ticket_id | P.ticket_number | cnt | 
+-------------+-------------------+-------------+-------------------+-----+ 
| 8489  | 14-21-28-32-48-49 | 2528  | 14-21-33-45-48-49 | 6 | 
| 8553  | 02-14-17-38-47-53 | 2364  | 02-30-38-44-47-53 | 6 | 
| 8615  | 05-12-29-33-36-43 | 4654  | 12-21-29-33-36-37 | 6 | 
| 8686  | 09-13-29-34-44-48 | 6038  | 09-13-17-29-33-44 | 6 | 
| 8693  | 01-10-14-17-42-50 | 5330  | 01-10-37-42-48-50 | 6 | 
| ...   | ...    | ...   | ...    | ... | 
| 19195  | 05-13-29-41-46-51 | 5106  | 07-13-14-29-41-51 | 6 | 
+-------------+-------------------+-------------+-------------------+-----+ 

Проблема

Проблема заключается в том, что я выполняю это для таблицы 10476 rows, что приводит к более загар 100 миллионов ticket_number против pickX, чтобы сравнить, продолжая около 172 секунд, чтобы завершить. Это слишком медленно.

ЗАДАЧА

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

Возможно ли это?

+1

Имея имена столбцов с номерами в нем почти всегда являются признаком плохого дизайна таблицы. –

+0

Итак, любые предложения? – juanba1984

+0

Да, измените дизайн стола. Вам нужен другой стол для ваших «выборов», что бы это ни было. –

ответ

1

Если вы хотите сохранить текущую структуру затем изменить pick1..6 к TinyInt типа вместо VARCHAR

TINYINT (1) сохраняет значения между -128 до 128, если он подписан. И тогда ваш запрос не будет иметь такой конкат с %, который является причиной медленного запуска.

Затем эти два запроса даст вам тот же результат

select * FROM ticket where pick1 = '8'; 
select * FROM ticket where pick1 = '08'; 

Это структура SQL:

CREATE TABLE `ticket` (
    `ticket_id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
    `ticket_number` varchar(30) DEFAULT NULL, 
    `pick1` tinyint(1) unsigned zerofill DEFAULT NULL, 
    `pick2` tinyint(1) unsigned zerofill DEFAULT NULL, 
    `pick3` tinyint(1) unsigned zerofill DEFAULT NULL, 
    `pick4` tinyint(1) unsigned zerofill DEFAULT NULL, 
    `pick5` tinyint(1) unsigned zerofill DEFAULT NULL, 
    `pick6` tinyint(1) unsigned zerofill DEFAULT NULL, 
    PRIMARY KEY (`ticket_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1; 

Я думаю, вы можете даже удалить zerofill

если не работает, измените дизайн таблицы.

+0

Теперь я пробовал: 0 select3. , где ((A.ticket_number как P.pick1) + (A.ticket_number как P.pick2) + (A.ticket_number как P.pick3) + (A.ticket_number как P.pick4) + (А .ticket_number как P.pick5) + (A.ticket_number как P.pick6)> 3) группа по A.ticket_id , имеющий CNT> 5; ' ... но, похоже, не работает, так как ни одна строка не возвращаются , – juanba1984

+0

В любом случае, я полностью открыт для изменения структуры таблицы и запроса. Есть идеи? – juanba1984

+1

Используйте 'TINYINT (2) UNSIGNED ZEROFILL' - это _one_ использование для« (n) »в целых числах. –

1

Насколько велики могут быть цифры? Похож на 50. Если ответ равен 63 или меньше, измените следующий формат:

Все 6 номеров хранятся в одном SET ('0','1','2',...,'50') и используют подходящие операции для установки n-го бита.

Тогда, сравнивая два комплекта, вы получите BIT_COUNT(x & y), чтобы узнать, сколько совпадений. Простое сравнение будет проверять на равенство.

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

Битовая арифметика может быть выполнена в SQL или на вашем клиентском языке. Например, чтобы построить SET для (11, 33, 7), код будет

INSERT INTO t SET picks = '11,33,7' -- order does not matter 

Кроме того, это будет работать:

... picks = (1 << 11) | 
      (1 << 33) | 
      (1 << 7) 

быстрый пример:

CREATE TABLE `setx` (
    `picks` set('1','2','3','4','5','6','7','8','9','10') NOT NULL 
) ENGINE=InnoDB; 
INSERT INTO setx (picks) VALUES ('2,10,6'); 
INSERT INTO setx (picks) VALUES ('1,3,5,7,9'), ('2,4,6,8,10'), ('9,8,7,6,5,4,3,2,1,10'); 
SELECT picks, HEX(picks+0) FROM setx; 
+----------------------+--------------+ 
| picks    | HEX(picks+0) | 
+----------------------+--------------+ 
| 2,6,10    | 222   | 
| 1,3,5,7,9   | 155   | 
| 2,4,6,8,10   | 2AA   | 
| 1,2,3,4,5,6,7,8,9,10 | 3FF   | 
+----------------------+--------------+ 
4 rows in set (0.00 sec) 
+0

Привет, Рик. Спасибо за ответ. Я посмотрю и вернусь с результатом. :) – juanba1984

Смежные вопросы