MySQL:索引不用于单个或连接查询

时间:2014-07-07 08:27:35

标签: mysql sql join database-design indexing

我使用的是MySQL 5.6.19版,我遇到了这个问题。 我不知道是否有任何我忘记的基本话题,或者它实际上是奇怪的。

好吧,我有客户,代码(与客户相关)和产品。 产品直接与客户或代码相关。

示例脚本是这样的:

    DROP TABLE IF EXISTS `CUSTOMERS`;
CREATE TABLE `CUSTOMERS` (
  `REQ_ID` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `CUSTOMER_ID` VARCHAR(15) COLLATE LATIN1_SPANISH_CI NOT NULL ,
  `SEGMENT_ID` INT(10) UNSIGNED NOT NULL,
  `STATUS` INT(10) UNSIGNED NOT NULL,
  PRIMARY KEY (`REQ_ID`),
  KEY `IDX_CUSTOMER_ID` (`CUSTOMER_ID`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=LATIN1 COLLATE=LATIN1_SPANISH_CI;

INSERT INTO `CUSTOMERS` (REQ_ID,CUSTOMER_ID,SEGMENT_ID,STATUS) VALUES (1,'CA.5310',1,1);
INSERT INTO `CUSTOMERS` (REQ_ID,CUSTOMER_ID,SEGMENT_ID,STATUS) VALUES (2,'KH.5880',1,1);
INSERT INTO `CUSTOMERS` (REQ_ID,CUSTOMER_ID,SEGMENT_ID,STATUS) VALUES (3,'QQ.4639',1,1);
INSERT INTO `CUSTOMERS` (REQ_ID,CUSTOMER_ID,SEGMENT_ID,STATUS) VALUES (4,'TH.7692',1,1);
INSERT INTO `CUSTOMERS` (REQ_ID,CUSTOMER_ID,SEGMENT_ID,STATUS) VALUES (5,'YU.6972',1,1);
INSERT INTO `CUSTOMERS` (REQ_ID,CUSTOMER_ID,SEGMENT_ID,STATUS) VALUES (6,'ZA.7926',1,1);

COMMIT;

DROP TABLE IF EXISTS `CUSTOMER_CODES`;
CREATE TABLE `CUSTOMER_CODES` (
  `REQ_ID` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `CODE_ID` VARCHAR(20) COLLATE LATIN1_SPANISH_CI NOT NULL,
  `CUSTOMER_ID` VARCHAR(15) COLLATE LATIN1_SPANISH_CI NOT NULL,
  PRIMARY KEY (`REQ_ID`),
  KEY `IDX_CODE_ID` (`CODE_ID`),
  KEY `IDX_CUSTOMER_ID` (`CUSTOMER_ID`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=LATIN1 COLLATE=LATIN1_SPANISH_CI;

INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (1,'AW-5332-R-918806','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (2,'CA-7600-F-419496','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (3,'DJ-6557-X-562485','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (4,'DL-6266-L-449657','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (5,'DS-9853-K-509896','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (6,'EF-6540-Z-075718','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (7,'EY-4689-L-798950','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (8,'HU-9798-W-580898','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (9,'HW-2352-Q-563124','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (10,'IJ-0691-P-347580','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (11,'IY-1366-C-744557','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (12,'JX-2997-A-619073','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (13,'KW-8547-V-632604','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (14,'LD-3972-X-404768','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (15,'OH-8414-Q-681690','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (16,'QB-3065-E-961964','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (17,'RS-8531-A-605359','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (18,'RT-0093-P-934263','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (19,'SR-9752-I-936573','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (20,'TT-1675-V-069568','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (21,'UX-9967-P-917243','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (22,'VB-1578-E-917704','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (23,'XT-9346-I-369510','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (24,'YD-5031-S-685069','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (25,'ZU-5811-D-519507','CA.5310');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (26,'AI-8578-P-297337','KH.5880');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (27,'BV-8935-R-335510','KH.5880');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (28,'CE-5544-B-140242','KH.5880');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (29,'DU-4606-X-292810','KH.5880');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (30,'HU-9125-V-564299','KH.5880');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (31,'JM-2356-C-286826','KH.5880');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (32,'KP-2970-D-251577','KH.5880');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (33,'OV-8244-W-588479','KH.5880');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (34,'RL-4228-G-538911','KH.5880');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (35,'VO-3663-O-065655','KH.5880');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (36,'YJ-8463-N-388866','KH.5880');
INSERT INTO `CUSTOMER_CODES` (REQ_ID,CODE_ID,CUSTOMER_ID) VALUES (37,'ZV-5622-T-443263','KH.5880');
COMMIT;

DROP TABLE IF EXISTS `CODE_OR_CUSTOMER_PRODUCTS`;
CREATE TABLE `CODE_OR_CUSTOMER_PRODUCTS` (
  `REL_ID` INT(11) NOT NULL AUTO_INCREMENT,
  `CODE_OR_CUSTOMER_ID` VARCHAR(20) COLLATE LATIN1_SPANISH_CI NOT NULL,
  `PRODUCT_ID` VARCHAR(55) COLLATE LATIN1_SPANISH_CI NOT NULL,
  `START_DATE` DATETIME NOT NULL,
  `END_DATE` DATETIME DEFAULT NULL,
  PRIMARY KEY (`REL_ID`),
  KEY `IDX_ENTIDAD` (`CODE_OR_CUSTOMER_ID`),
  KEY `IDX_ENTIDAD_FECHAS` (`CODE_OR_CUSTOMER_ID`,`START_DATE`,`END_DATE`)
) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=LATIN1 COLLATE=LATIN1_SPANISH_CI;

INSERT INTO `CODE_OR_CUSTOMER_PRODUCTS` (REL_ID,CODE_OR_CUSTOMER_ID,PRODUCT_ID,START_DATE,END_DATE) VALUES (1,'AW-5332-R-918806','DISC_01','2013-12-15 00:00:00',NULL);
INSERT INTO `CODE_OR_CUSTOMER_PRODUCTS` (REL_ID,CODE_OR_CUSTOMER_ID,PRODUCT_ID,START_DATE,END_DATE) VALUES (2,'AW-5332-R-918806','DISC_02','2014-01-13 00:00:00','2014-01-29 00:00:00');
INSERT INTO `CODE_OR_CUSTOMER_PRODUCTS` (REL_ID,CODE_OR_CUSTOMER_ID,PRODUCT_ID,START_DATE,END_DATE) VALUES (3,'AW-5332-R-918806','DISC_02','2014-02-01 00:00:00',NULL);
INSERT INTO `CODE_OR_CUSTOMER_PRODUCTS` (REL_ID,CODE_OR_CUSTOMER_ID,PRODUCT_ID,START_DATE,END_DATE) VALUES (4,'CA.5310','DISC_03','2013-01-01 00:00:00',NULL);

COMMIT;

我正在尝试这种方法,因为我不想将客户代码关系带到CODE_OR_CUSTOMERS_PRODUCTS表。

当我为此查询运行 EXPLAIN 时:

EXPLAIN SELECT 
CODE_OR_CUSTOMER_ID, 
PRODUCT_ID
FROM CODE_OR_CUSTOMER_PRODUCTS
where 
    ( 
    CODE_OR_CUSTOMER_ID = 'CA.5310'
    or
    CODE_OR_CUSTOMER_ID IN (
                SELECT CODE_ID
                FROM CUSTOMERS, CUSTOMER_CODES
                where CUSTOMERS.CUSTOMER_ID = CUSTOMER_CODES.CUSTOMER_ID
                and CUSTOMERS.CUSTOMER_ID = 'CA.5310'
            )
    )
    and START_DATE <= '2014-01-31' and (END_DATE IS NULL OR '2014-01-01' <= END_DATE)

我已获得对CODE_OR_CUSTOMERS_PRODUCTS的全表访问权限:

*'1', 'PRIMARY', 'CODE_OR_CUSTOMER_PRODUCTS', '**ALL**', 'IDX_ENTIDAD,IDX_ENTIDAD_FECHAS', NULL, NULL, NULL, '4', 'Using where'*

我认为这是因为 IN 子句,但我不想查询和重新查询单个CUSTOMER_ID及其相关CODE_ID的次数(可能是数百次)。

如何避免此所有表访问? 我需要一些新鲜的眼睛,或者可能是另一种方法。 任何建议都会非常非常有用。

非常感谢

2 个答案:

答案 0 :(得分:1)

可以使用JOIN消除IN子句。但是也应该可以通过将2个更简单的查询联合在一起来删除OR: -

SELECT CODE_OR_CUSTOMER_ID, 
    PRODUCT_ID
FROM CODE_OR_CUSTOMER_PRODUCTS
WHERE CODE_OR_CUSTOMER_ID = 'CA.5310'
AND START_DATE <= '2014-01-31' AND (END_DATE IS NULL OR '2014-01-01' <= END_DATE)
UNION
SELECT CODE_OR_CUSTOMER_ID, 
    PRODUCT_ID
FROM CODE_OR_CUSTOMER_PRODUCTS
INNER JOIN CUSTOMER_CODES ON CUSTOMER_CODES.CODE_ID = CODE_OR_CUSTOMER_PRODUCTS.CODE_OR_CUSTOMER_ID
INNER JOIN CUSTOMERS ON CUSTOMERS.CUSTOMER_ID = CUSTOMER_CODES.CUSTOMER_ID AND CUSTOMERS.CUSTOMER_ID = 'CA.5310'
WHERE  START_DATE <= '2014-01-31' AND (END_DATE IS NULL OR '2014-01-01' <= END_DATE)

这似乎忽略了测试数据的索引,但我怀疑这取决于有限的数据。

答案 1 :(得分:0)

使用CODE_OR_CUSTOMER_PRODUCTS中没有索引的原因是因为您在选择列表中有PRODUCT_ID并且它不包含在任何索引中。因此,一旦找到符合条件的行,就必须执行另一个逻辑IO以获取该字段的值。由于行很小且行数很少,优化器只需在整个表中读取一个swell foop并保存一些IO。如果您的实际表包含许多行和/或行包含许多您为简单起见而省略的字段,那么IDX_ENTIDAD_FECHAS索引可能会用于搜索,并根据需要执行额外的逻辑IO(名义上只占很小的百分比)时间;也就是说,很少有行通过过滤器。)

您可以将PRODUCT_ID添加到索引中,从而使其成为完整的覆盖索引。然后使用索引,我保证。

如果可以,这是一个非常尴尬的设计。但这不是你的关注点,所以我会对此发表评论。但是,在处理具有Startxxx / Endxxx列的设计时,通常搜索的日期位于开始/结束日期之间:search_date >= START and search_date < END。如果是这种情况,那么您拥有START_DATE <= '2014-01-31''2014-01-01' <= END_DATE,实际上就是START_DATE >= '2014-01-01''2014-01-31' > END_DATE。否则,您的WHERE子句没有意义。