1

我已经让这个查询运行得很好,但问题是当我的 4 个表变得太大时,它会变得很慢。

我该如何优化呢?

SELECT 
    all_records.user_id,
    users.NAME,
    users.IMAGE
FROM (
    SELECT user_id FROM comments
    WHERE commentable_id   = #{object.id}
      AND commentable_type = '#{object.class.to_s}'
    UNION ALL
    SELECT user_id FROM hello
    WHERE helloable_id     = #{object.id}
      AND helloable_type   = '#{object.class.to_s}'
    UNION ALL
    SELECT user_id FROM foo
    WHERE fooable_id       = #{object.id}
      AND fooable_type     = '#{object.class.to_s}'
    UNION ALL
    SELECT user_id FROM bar
    WHERE barable_id       = #{object.id}
      AND barable_type     = '#{object.class.to_s}'
) AS all_records
INNER JOIN users ON users.id = all_records.user_id
GROUP BY
    all_records.user_id,
    users.NAME,
    users.IMAGE
LIMIT 15

查询应该做的是获取在 (4) 表上执行某些操作的唯一用户(请原谅表名称的更改)。即使使用LIMIT 15它仍然运行缓慢,因为我认为它仍然可以读取所有 4 个表。我这样做是对的还是有什么方法可以优化它?

供参考:我正在使用 postgres 并使用 rails 但在find_by_sql.

编辑

本地 postgres:9.0.5;heroku postgres:9.1

4

1 回答 1

1

照原样回答您的问题:“获取 15 行任意行”。那应该非常快。

SELECT u.id, u.name, u.image
FROM  (
   SELECT id
   FROM  (
      SELECT user_id AS id
      FROM   comments
      WHERE  commentable_id   = #{object.id}
      AND    commentable_type = '#{object.class.to_s}'

      UNION ALL
      SELECT user_id
      FROM   hello
      WHERE  helloable_id   = #{object.id}
      AND    helloable_type = '#{object.class.to_s}'

      UNION ALL
      SELECT user_id
      FROM   foo
      WHERE  fooable_id     = #{object.id}
      AND    fooable_type   = '#{object.class.to_s}'

      UNION ALL
      SELECT user_id
      FROM   bar
      WHERE  barable_id     = #{object.id}
      AND    barable_type   = '#{object.class.to_s}'
      ) AS a
   GROUP  BY id
   LIMIT  15
   ) b
JOIN   users u USING (id)
  • 如果您正在运行 PostgreSQL 9.1 或更高版本,则可以简化为GROUP BY id,假设users.id是主键。但我采取了更激进的方法。

  • 我调高了GROUP BY一个LIMIT查询级别,希望能够对基表进行更快的索引扫描。LIMIT 15ORDER BY应该发生顺序扫描。Postgres 可以从索引顶部读取元组,并在达到限制后立即停止。
    与此密切相关的案例类似:在结果可用之前尝试多个 SELECT 的方法?
    只有在这里 Postgres 从索引中读取元组。

  • might可以通过使用LEFT JOIN users而不是JOIN(而不是我的额外子查询级别)来实现相同的效果,因为它JOIN可能会从结果中删除行并禁用更简单的查询计划。

  • 为了获得完美的性能,你有像这样的索引

    CREATE INDEX comments_mult_idx
    ON comments (commentable_id, commentable_type, user_id)
    

    在所有 4 张桌子上。user_id必须是最后一列。这就是为什么

于 2013-02-12T14:53:12.897 回答