由于选择性低(全为 NULL),MariaDB 不在 1 列自连接上使用索引

Posted

技术标签:

【中文标题】由于选择性低(全为 NULL),MariaDB 不在 1 列自连接上使用索引【英文标题】:MariaDB doesn't use index on 1 column self-join due to low selectivity (all NULLs) 【发布时间】:2016-03-10 07:40:44 【问题描述】:

我们有一个查询,它根据一个很少可用的标识符在我们的一个表中查找重复项,我们称之为rareIdentifier INT(10) UNSIGNED NULL。我们在此列上有一个单列常规旧索引。

有问题的查询如下所示:

SELECT a.id, b.id FROM
    widget a INNER JOIN widget b
ON a.rareIdentifier = b.rareIdentifier;

问题在于,对于最近的重复查找运行,我们实际上有 0 行,其值为 rareIdentifier;即该列的所有行都有NULL。 MariaDB 决定不使用索引,而是选择了扫描整个表的Using join buffer (flat, BNL join) 方法。

但是NULLs 不能互相平等!那么它为什么要尝试比较每一对行呢?

我知道如果 mysql/MariaDB 的选择性太低,它不会使用索引。我相信这里就是这种情况。事实上,索引中只有 1 个值似乎意味着查询几乎是即时的。

该表是一个 InnoDB 表。

【问题讨论】:

你当前对该表的索引是什么? 【参考方案1】:

InnoDB 可能不够聪明,无法意识到与NULL 相比总是NULL,因此是虚假的。也许它只是决定“所有值都相同,因此它们必须相等”(但实际上我真的不知道)。

作为一种解决方法,添加 ... AND a.rareIdentifier IS NOT NULL 应该会给优化器足够的提示。

【讨论】:

【参考方案2】:

在大多数情况下,这可能会更快,特别是如果有许多行具有相同的rareIdentifier

SELECT  rareIdentifier, MIN(id), MAX(id), COUNT(*)
    FROM tbl
    WHERE rareIdentifier IS NOT NULL
    GROUP BY rareIdentifier
    HAVING COUNT(*) > 1;

或者您可以使用GROUP_CONCAT(id) 代替最小值和最大值。 (但是,如果有很多 dup,列表将被截断。)

假设 InnoDB 和 INDEX(rareIdentifier),这个 SELECT 应该是一个非常有效的索引“范围”扫描。

回到你的问题...

实际上有 0 行...MariaDB 决定不使用索引

我以前在旧版本的 MySQL 中经常看到这种情况。我想知道 Oracle 是否修复了,但 MariaDB 没有修复。

【讨论】:

以上是关于由于选择性低(全为 NULL),MariaDB 不在 1 列自连接上使用索引的主要内容,如果未能解决你的问题,请参考以下文章

MySQL/MariaDB 中的存储过程不允许使用 NULL 参数

选择列,不包括一些全为NA的列

Mysql MariaDB安装

源码包编译安装mariaDB

java开发 怎么判断list集合中的元素全为null

Linux安装mariadb二进制版本