OR 语句大大减慢了查询速度

Tar*_*len 4 sql postgresql

OR如果像这样包含在 where 子句中,我的查询的行为会非常不同

SELECT t.id
FROM teams AS t
LEFT OUTER JOIN teams_users AS tu ON tu.team_id = t.id
LEFT OUTER JOIN user AS u ON tu.user_id = u.id
WHERE 
    u.id = '8601680c9cd549c0a786288e1775fb63' OR t.id = '8601680c-9cd5-49c0-a786-288e1775fb63'
Run Code Online (Sandbox Code Playgroud)

以上将在约 500 毫秒内运行。如果我删除 OR 并仅按用户 ID 进行过滤,如下所示:

WHERE u.id = '8601680c9cd549c0a786288e1775fb63'
Run Code Online (Sandbox Code Playgroud)

它运行时间为 20 毫秒。如果我只按团队 ID 进行过滤,如下所示:

WHERE t.id = '8601680c-9cd5-49c0-a786-288e1775fb63'
Run Code Online (Sandbox Code Playgroud)

它的运行时间也为 20 毫秒。但由于某种原因,如果我同时过滤,它会变得慢得多

有什么想法吗?

Mat*_*lie 5

在许多情况下,这是因为解释计划无法轻松处理这两个条件。

  • 一种情况的最佳计划将与另一种情况的计划显着不同。

要了解这一点或确定您需要查看所有三个解释计划,如果您希望我们弄清楚这一点,您需要在您的问题中提供它们。

  1. 您主要查询的是OR
  2. 仅过滤t.id
  3. 仅过滤u.id

使用的示例IN()避免了键入关键字OR,但仍然产生相同的逻辑,并且通常容易出现相同的规划问题。

如果是这种情况,一种选择是使用UNION. 这允许您的结果包含来自两个单独查询的结果,DBMS 可以单独计划......

SELECT t.id
FROM teams AS t
WHERE t.id = '8601680c-9cd5-49c0-a786-288e1775fb63'

UNION

SELECT t.id
FROM teams AS t
INNER JOIN teams_users AS tu ON tu.team_id = t.id
INNER JOIN user AS u ON tu.user_id = u.id
WHERE u.id = '8601680c9cd549c0a786288e1775fb63'
Run Code Online (Sandbox Code Playgroud)

这也让LEFT OUTER JOINs变成了INNER JOINs。这是解释计划在第一个示例中会受到影响的示例;需要LEFT JOIN只是因为您想解决这两个条件,但允许它们单独解决允许INNER JOIN

您还可以删除第二个查询中的连接teams,然后选择tu.team_id AS id...

编辑:

SELECT id
  FROM teams
 WHERE id = '8601680c-9cd5-49c0-a786-288e1775fb63'

UNION

SELECT team_id
  FROM teams_users
 WHERE user_id = '8601680c9cd549c0a786288e1775fb63'
Run Code Online (Sandbox Code Playgroud)

我还注意到您用于过滤表的列teams也是您从中选择的列。那么,您真的需要参考团队表吗SELECT '8601680c-9cd5-49c0-a786-288e1775fb63' AS id UNION ...)(或者您只是检查这样的团队是否存在?)