在 PostgreSQL 中使用 LEFT JOIN 而不是 NOT IN

Posted

技术标签:

【中文标题】在 PostgreSQL 中使用 LEFT JOIN 而不是 NOT IN【英文标题】:Using LEFT JOIN instead of NOT IN in PostgreSQL 【发布时间】:2019-03-16 01:15:17 【问题描述】:

我正在调试 django-reversion(一个 django 库)中可能存在的性能错误。我遇到的问题是每次我运行 django-reversion 的createinitialrevisions 时,我的数据库都会花费大量时间来处理正在发生的事情。

我在 RDS 中启用了性能洞察,我看到正在杀死我的数据库的查询如下所示:

SELECT "table_a"."id"
FROM "table_a"
WHERE NOT (CAST("table_a"."id" as text) IN (
        SELECT U0."object_id"
        FROM "reversion_version" U0
        WHERE (U0."content_type_id" = 49 AND U0."db" = 'default')
))

如果我正确理解了我在https://explainextended.com/2009/09/16/not-in-vs-not-exists-vs-left-join-is-null-postgresql/ 上读到的内容,那么 PostgreSQL 无法像NOT IN 那样优化LEFT JOIN。这就是为什么我决定重写这个查询,看看它是否会花费相同的时间来运行。

这是改写后的结果:

SELECT "table_a"."id"
FROM "table_a"
LEFT JOIN 
        "reversion_version" U0
ON U0."object_id" = "table_a"."id"::text
WHERE U0."object_id" IS NULL AND U0."content_type_id" = 49 AND U0."db" = 'default'

我一定是做错了什么,因为我得到了不同的结果。我的查询(重写的)根本没有返回任何内容。

我错过了什么?

【问题讨论】:

请您的 Postgres 版本以及 table_areversion_version 的表定义(CREATE TABLE 语句)。 如果右侧表(U0)中没有匹配记录,则U0."content_type_id"和U0."db"将包含NULL 警告:你发现的文章是 10 年前为 Postgres 8.4 写的,那是古老的。虽然一些基础知识仍然是正确的,但很多都不再适用了。除非您使用的是那个古老版本的 Postgres。你不应该这样做。 了解左连接返回的内容:行上的内连接以及由空值扩展的不匹配左表行。作为左连接的一部分,始终知道您想要什么内连接。在左连接删除任何由空值扩展的行之后,内连接或需要右表列不为空的地方,即仅在行上留下内连接,即“将外连接变为内连接”。你有那个。除了要求 U0.object_id 既不为空也不为空的行。 PS 请在代码问题中提供minimal reproducible example。 注意:CAST("table_a"."id" as text) IN ( 您正在尝试对文本字段进行 (ANTI-) 连接CASTed((int?)field)。不要指望这会很快,这里不能使用索引。另外:在您的第一个查询中有一个额外的)。它会失败(非常快!) 【参考方案1】:

正确重写的查询需要前一个子查询的WHERE 条件作为LEFT JOIN 的连接条件,例如:

SELECT table_a.id
FROM   table_a
LEFT   JOIN  reversion_version U0 ON U0.object_id = table_a.id::text
                                 AND U0.content_type_id = 49
                                 AND U0.db = 'default'
WHERE  U0.object_id IS NULL;

您尝试的方式是一个逻辑矛盾:它会要求table_a 中的行而reversion_version 中没有匹配的行,并且然后 对不存在的行施加附加条件。那永远不会返回任何行。

必须反过来:在table_a 中找到满足上述条件但在reversion_version 中没有匹配行的行。因此,将这些条件从WHERE 子句移到LEFT JOIN 的连接子句中。微妙但根本的区别。

见:

SQL / PostgreSQL left join ignores "on = constant" predicate, on left table Explain JOIN vs. LEFT JOIN and WHERE condition performance suggestion in more detail Select rows which are not present in other table

关于性能可能还有很多话要说,但并非没有必要的设置细节...

【讨论】:

以上是关于在 PostgreSQL 中使用 LEFT JOIN 而不是 NOT IN的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Joi 字符串验证中使用枚举值

PostgreSql Rectangle DataType 用于存储 Left、Top、Width 和 Height

koa中使用joi进行参数校验

JOI:允许数组中的空值

postgresql出错could not create semaphores: No space left on device

postgresql出错could not create semaphores: No space left on device