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

MariaDB doesn't use index on 1 column self-join due to low selectivity (all NULLs)

我们有一个查询在我们的 table 之一中查找重复项,基于一个很少可用的标识符,我们称之为 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) 扫描整个 table.

的方法

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

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

table 是一个 InnoDB table。

InnoDB 可能不够聪明,无法意识到与 NULL 的比较总是 NULL,因此是错误的。也许它只是决定"all values are the same, so they must be equal"(但其实我真的不知道)。

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

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

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

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

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

回到你的问题...

actually had 0 rows ... MariaDB decided not to use the index

我以前在 MySQL 的旧版本中看到很多。我想知道 Oracle 是否已修复,但 MariaDB 未修复。