在join on子句中提到字段时未获取mysql索引

时间:2018-08-27 12:38:19

标签: mysql indexing

explain select * from users u join wallet w on w.userId=u.uuid where w.userId='8319611142598331610'; //Index is taken

enter image description here

explain select * from users u join wallet w on w.userId=u.uuid where w.currencyId=8; //index is not taken

enter image description here

从上面可以看出,索引userIdIdx用于后一种情况,但不使用前一种情况。

以下是两个表的架构-

CREATE TABLE `users` (
  `uuid` varchar(600) DEFAULT NULL,
  KEY `uuidIdx` (`uuid`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `wallet` (
  `Id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` varchar(200) NOT NULL DEFAULT '',
  `currencyId` int(11) NOT NULL,
  PRIMARY KEY (`Id`),
  KEY `userIdIdx` (`userId`),
  KEY `currencyIdIdx` (`currencyId`)
) ENGINE=InnoDB AUTO_INCREMENT=279668 DEFAULT CHARSET=latin1;

如何强制MySql考虑userIdIdxuuidIdx索引?

2 个答案:

答案 0 :(得分:1)

有两种方法可以改善此问题。

方法1:

为两个查询添加多列索引wallet(userId, currencyId)似乎更好。

参见演示https://www.db-fiddle.com/f/aesNYevEzwopmXrnQJRPoS/0

方法2

重写查询。
这适用于当前的表结构。

查询

SELECT 
 *
FROM (
  SELECT 
   wallet.userId
  FROM 
   wallet 
  WHERE
   wallet.currencyId = 8
) AS wallet
INNER JOIN 
 users
ON
 wallet.userId = users.uuid

参见演示https://www.db-fiddle.com/f/aesNYevEzwopmXrnQJRPoS/3

p.s我还建议您在将InnoDB用作表引擎时也向用户表中添加Id int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY。 我的这篇帖子解释了为什么https://dba.stackexchange.com/a/48184/27070

答案 1 :(得分:0)

这两个查询在您提供的内容上都尽力而为。

select  *
    from  users u
    join  wallet w  ON w.userId=u.uuid
    where  w.userId='8319611142598331610';

select  *
    from  users u
    join  wallet w  ON w.userId=u.uuid
    where  w.currencyId=8;

如果表中只有一行(例如users),则优化器采用不同的路径。这似乎是第一个查询发生的情况。

否则,由于正在进行过滤,两个查询都将以wallet开头。 wallet中的每个辅助键对于其中一个查询都很方便。甚至会更好

INDEX(userId, currencyId, id)  -- for first query
INDEX(currencyId, userId, id)  -- for second query

第一列用于WHERE;其他两列使索引“覆盖”,因此它不需要在索引和数据之间反弹。

(Geez,这些表的列数很少。)

w中进行过滤后,它将移至u并使用INDEX(uuid)。由于那是表中的唯一列(没有名称?),它可以是“正在使用索引”,即“正在覆盖”。

进入u的唯一原因是验证是否存在一个值与w.userId匹配的用户。既然您可能总是这样,为什么在查询中从JOINusers呢?