在PostgreSQL中使用LEFT JOIN代替NOT IN

时间:2019-03-16 01:15:17

标签: sql postgresql left-join notin

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

我已在RDS中启用了Performance Insights,并且看到正在杀死数据库的查询如下所示:

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'

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

我想念什么?

1 个答案:

答案 0 :(得分:2)

正确重写的查询需要前子查询的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的join子句。细微但基本的差异。

请参阅:

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