当存在 NULL 时,性能选择与另一个表中的条目不匹配的行

Posted

技术标签:

【中文标题】当存在 NULL 时,性能选择与另一个表中的条目不匹配的行【英文标题】:Performance selecting rows not matching entry in another table when NULL is present 【发布时间】:2013-08-14 13:45:57 【问题描述】:

相关问题: How to select rows with no matching entry in another table?

我试图使用此方法选择行,但无法使其在 SQLite 中工作。经过一番争论,我想到原因可能是某些字段中有 NULL 值。果然,我是对的,当我在下面的查询中将 = 更改为 IS 时,事情开始按预期运行:

CREATE TEMP TABLE newEvent(id INTEGER,t INTEGER,name,extra,extra2,extra3);
INSERT INTO newEvent(id,t,name,extra,extra2,extra3) VALUES
                           (0, 1376351146, 'TEST', NULL, NULL, NULL),
                           (0, 1376348867, 'OLD', NULL, NULL,NULL);
SELECT n.id,n.t,n.name,n.extra,n.extra2,n.extra3 FROM newEvent n 
       LEFT JOIN event E ON n.t = E.t AND n.name IS E.name
                                      AND n.extra IS E.extra;
                                      AND n.extra2 IS E.extra2;
                                      AND n.extra3 IS E.extra3
       WHERE E.id IS NULL;
DROP TABLE newEvent;

在上面的示例中,表event 中存在一条现有记录name='OLD'newEvent 表的定义与原始 event 表相同。

但是,我注意到一个大问题:我的查询现在需要将近 30 秒才能运行!如果我n.name IS E.name 更改为n.name = E.name,但将所有其他ISes 保持原样,则查询只需要大约400 毫秒。 (event 表中有大约 300 万条记录。)

为什么性能差异很大?事实证明,我实际上可以使用= 而不是IS 进行name 比较,因为它永远不会为空,但如果它曾经为空,它似乎会中断。相反,我担心在某些时候查询可能会开始运行缓慢,因为我不明白 name 是什么让相等查询运行得更快。我的猜测是,也许 SQLite 以某种方式知道额外字段中有空值并且能够进行优化,但我想要一些比疯狂猜测更坚定的东西。

据我所知,IS 只是 = 附加条件是它将 NULL 比较视为空字符串(假设没有实际的空字符串要比较)。那么为什么在名称字段上使用= 会快 75 倍,但对额外字段的性能没有影响???

【问题讨论】:

为什么不比较id 列?你想匹配的行可以不同吗? @Andriy name 和额外的字段都是无类型的(无论如何,类型对 SQLite 来说是更多的“建议”)。 Name 总是一个字符串,而 extras 都是字符串、数字和 NULL。它确实有所不同,例如,SQLite 不会将“200”匹配为等于 200。 @CL 我不匹配该行,因为它是自动的,并且在插入发生之前我不知道它。我基本上是在试图防止同一条记录被插入两次(在我使用的架构中极少数情况下是可能的)。其他字段足以唯一标识一条记录。 谢谢,不知道无类型列的事情,这是 SQLite 的一个奇怪的方面。 (并删除我特别嘈杂的第一条评论。) 【参考方案1】:

在连接中,SQLite 可以使用索引查找优化=,但不能优化IS。 此外,在单个查询中每个表不能使用多个索引。

因此,要么您没有包含 nameextra* 的多列索引,要么附加列的选择性不够高。

您可以使用compound SELECT 尝试完全不同的查询:

SELECT t, name, extra, extra2, extra3 FROM newEvent
EXCEPT
SELECT t, name, extra, extra2, extra3 FROM event

但是,这不允许您获得不比较的列(例如您的 id)。

【讨论】:

奇怪的是,名称或额外字段上没有索引...但是 t 字段上有一个。

以上是关于当存在 NULL 时,性能选择与另一个表中的条目不匹配的行的主要内容,如果未能解决你的问题,请参考以下文章

c# 从数据库中过滤出 NULL 行并更新现有行

如果迁移文件不存在但迁移表中的条目可用,我如何解决迁移问题

如果任何列与另一个表中的匹配行不同,如何插入行

从一个表中选择所有元素,并检查它们是不是与另一个表匹配

删除不同表中的条目 MySQL 查询

选择表中的列与另一个表中的列不同的数据