在MySQL

时间:2016-10-11 13:51:57

标签: mysql group-by inner-join innodb

描述

我有一个类似于以下的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    |
+-----------+-------------------+-------+-------+-------+-------+-------+-------+

现在,我的目标是根据ticket_number字段中各自的值(每组6个元素,按-分割,将表中的每个票证(自身除外)进行比较。 )。换句话说,例如,假设我将ticket_id = 655ticket_id = 658进行比较,就其各自的ticket_number字段中的元素而言,那么我会发现元素08和{{ 1}}出现在两个集合中。如果我们现在将24ticket_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_idP.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 | +-------------+-------------------+-------------+-------------------+-----+ 的表执行此操作,导致更多的棕褐色1百万10476 rowsticket_number进行比较,总共持续约172秒结束。这太慢了。

目标

我的目标是尽快完成执行,以便在不到一秒的时间内完成,因为这必须是实时的。

这可能吗?

2 个答案:

答案 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

如果这不起作用,请更改表格设计。

答案 1 :(得分:1)

数字有多大?看起来像50.如果答案是63或更少,那么将格式更改为:

所有6个数字都存储在一个SET ('0','1','2',...,'50')中,并使用合适的操作来设置第n位。

然后,比较两个集合变为BIT_COUNT(x & y)以找出多少匹配。一个简单的比较将测试相等性。

如果您的目标是查看表中是否已有特定的抽奖猜测,那么请对该列进行索引,以便查找速度很快。我不是指几分钟甚至几秒钟,而是几毫秒。即使是十亿行。

位算术可以用SQL或客户端语言完成。例如,要为(11,33,7)构建SET,代码将为

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)