优化查询以从不同的表中获取唯一(用户)记录

时间:2013-02-12 11:56:32

标签: sql ruby-on-rails postgresql query-optimization

我已经把这个查询运行得很完美了,但问题是当我的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中执行它。

修改

当地的帖子:9.0.5; heroku postgres:9.1

1 个答案:

答案 0 :(得分: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 BYLIMIT一个查询级别,希望能够在基表上实现更快的索引扫描。使用LIMIT 15且无ORDER BY顺序扫描不应发生。 Postgres只能从索引顶部读取元组,并在达到限制时立即停止 与此类似的情况类似:Way to try multiple SELECTs till a result is available?
    只有在这里Postgres从索引中读取元组。

  • might使用LEFT JOIN users代替JOIN(而不是我的额外子查询级别),JOIN可能会删除行,CREATE INDEX comments_mult_idx ON comments (commentable_id, commentable_type, user_id) 达到相同的效果从结果中删除一个更简单的查询计划。

  • 为了获得完美的性能,您有

    之类的索引
    user_id

    在所有4个表上。 {{1}}必须是最后一栏。 Here's why