我正在调试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'
我一定做错了,因为我得到了不同的结果。我的查询(重写的)根本没有返回任何内容。
我错过了什么?
正确重写的查询需要前子查询的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
的连接条款。微妙但根本的区别。
看到:
可能还有更多关于性能的说法,但并非没有您设置的必要细节......