MySQL:为啥将主键与不​​使用索引的随机生成的数字进行比较?

Posted

技术标签:

【中文标题】MySQL:为啥将主键与不​​使用索引的随机生成的数字进行比较?【英文标题】:MySQL : Why is comparing primary key to a randomly generated number not using the index?MySQL:为什么将主键与不​​使用索引的随机生成的数字进行比较? 【发布时间】:2012-09-11 07:42:52 【问题描述】:

尝试根据无孔的自动递增主键从表中选择随机行。

表架构:

CREATE TABLE IF NOT EXISTS `testTable` (
  `id` int(9) NOT NULL AUTO_INCREMENT,
  `data` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=0 ;

INSERT INTO `testTable` (`id`, `data`) VALUES
(1, 'hello'),
(2, 'world'),
(3, 'new'),
(4, 'data'),
(5, 'more and more'),
(6, 'data '),
(7, 'more rows here'),
(8, 'most rows here'),
(9, 'testing'),
(10,'last');

查询:

1/explain select * from testTable where id = ceil(Rand()*10) limit 1 ;

http://sqlfiddle.com/#!2/6e2b1/1

结果:

| ID | SELECT_TYPE |     TABLE | TYPE | POSSIBLE_KEYS |    KEY | KEY_LEN |    REF | ROWS |       EXTRA |
--------------------------------------------------------------------------------------------------------
|  1 |      SIMPLE | testTable |  ALL |        (null) | (null) |  (null) | (null) |   10 | Using where |

2/explain select * from testTable where id = 7 limit 1 ;

http://sqlfiddle.com/#!2/6e2b1/2

结果:

| ID | SELECT_TYPE |     TABLE |  TYPE | POSSIBLE_KEYS |     KEY | KEY_LEN |   REF | ROWS | EXTRA |
---------------------------------------------------------------------------------------------------
|  1 |      SIMPLE | testTable | const |       PRIMARY | PRIMARY |       4 | const |    1 |       |

为什么 query#1 不使用索引,而 ceil(rand()*10) 应该理想地评估为一个常量,然后可以将其与主键进行比较?优化器不应该那样工作吗?还是我在这里遗漏了一些明显的东西。

【问题讨论】:

在 WHERE 条件下使用函数(如 ceil/rand)会导致系统首先获取所有行,以便能够将结果与该函数的每个结果进行比较。您最好使用“外部”选择来获取随机整数值,然后使用它来获取您的主键。 【参考方案1】:

该键不能用于该查询,因为每行都会调用RAND(),并且每次都返回不同的值。

你可以试试这个代码:

SET @rand_value := CEIL(RAND()*10);
EXPLAIN SELECT * FROM testTable WHERE id = @rand_value;

它首先计算一个随机值并将其分配给一个变量,然后在查询中使用它。 正如 aneroid 所指出的,LIMIT 1 是无用的:由于该条件适用于主键,因此查询将永远不会返回超过一行。

使用此查询,输出为:

| ID | SELECT_TYPE |     TABLE |  TYPE | POSSIBLE_KEYS |     KEY | KEY_LEN |   REF | ROWS | EXTRA |
---------------------------------------------------------------------------------------------------
|  1 |      SIMPLE | testTable | const |       PRIMARY | PRIMARY |       4 | const |    1 |       |

【讨论】:

+1 推荐用法的好例子。在这种情况下,因为id 是主键,所以不需要LIMIT 1 :-)【参考方案2】:

来自mysql documentation for RAND()

每次执行 WHERE 时,都会重新评估 WHERE 子句中的

RAND()

所以不是将主键与常量进行比较,而是每次都在变化的值(在这种情况下,对于每一行)。如果您在查询中删除 LIMIT 1,您将看到更多行匹配不同的 PK - 这表明“每次都重新评估”行为。

编辑:请参阅 Jocelyn 的示例,作为首先生成随机数然后获取匹配 PK id 的行(不需要LIMIT 1,顺便说一句)。 Najzero 的评论中也有类似说明。

【讨论】:

以上是关于MySQL:为啥将主键与不​​使用索引的随机生成的数字进行比较?的主要内容,如果未能解决你的问题,请参考以下文章

MySQL主键与索引的区别和联系

mysql,主键与索引的区别和联系

mysql中,主键与普通索引

[MySQL] innoDB引擎的主键与聚簇索引

primary key主键与unique键的区别以及作用?

mysql的联合主键与复合主键区别