如何改进此SQL查询

时间:2014-03-04 17:19:27

标签: sql postgresql indexing postgresql-performance sql-limit

我有这个查询

SELECT "items".*
FROM
    "items"
    INNER JOIN
    item_mods ON item_mods.item_id = items.id AND item_mods.mod_id = 15
WHERE (items.league_id = 1) AND (items.item_type_id = 11) AND (num_sockets >= 2)
ORDER BY item_mods.total_value DESC
LIMIT 25

这是解释http://explain.depesz.com/s/dbf

"Limit  (cost=55739.84..55739.90 rows=25 width=554) (actual time=18065.470..18065.478 rows=25 loops=1)"
"  ->  Sort  (cost=55739.84..55741.90 rows=824 width=554) (actual time=18065.468..18065.471 rows=25 loops=1)"
"        Sort Key: item_mods.total_value"
"        Sort Method: top-N heapsort  Memory: 37kB"
"        ->  Nested Loop  (cost=5871.95..55716.59 rows=824 width=554) (actual time=285.806..18055.589 rows=610 loops=1)"
"              ->  Bitmap Heap Scan on items  (cost=5871.52..20356.70 rows=4339 width=550) (actual time=201.543..10028.684 rows=9945 loops=1)"
"                    Recheck Cond: ((item_type_id = 11) AND (num_sockets >= 2))"
"                    Rows Removed by Index Recheck: 4120"
"                    Filter: (league_id = 1)"
"                    Rows Removed by Filter: 1125"
"                    ->  BitmapAnd  (cost=5871.52..5871.52 rows=4808 width=0) (actual time=199.322..199.322 rows=0 loops=1)"
"                          ->  Bitmap Index Scan on index_items_on_item_type_id  (cost=0.00..289.61 rows=15625 width=0) (actual time=38.699..38.699 rows=16018 loops=1)"
"                                Index Cond: (item_type_id = 11)"
"                          ->  Bitmap Index Scan on index_items_on_num_sockets  (cost=0.00..5579.49 rows=301742 width=0) (actual time=158.441..158.441 rows=301342 loops=1)"
"                                Index Cond: (num_sockets >= 2)"
"              ->  Index Scan using index_item_mods_on_item_id on item_mods  (cost=0.43..8.14 rows=1 width=8) (actual time=0.803..0.803 rows=0 loops=9945)"
"                    Index Cond: (item_id = items.id)"
"                    Filter: (mod_id = 15)"
"                    Rows Removed by Filter: 9"
"Total runtime: 18065.773 ms"

如何提高此查询的速度?我注意到有一个循环>索引扫描9000次

3 个答案:

答案 0 :(得分:2)

查询速度慢的原因是因为没有索引可以返回数据。

注意“位图索引扫描”,它说,我知道你有一个索引,但我必须查看整个表以找到我需要的行(因此总行扫描最多为301742!)。这可能是因为您要求的其他列的组合以及您正在应用的约束,即item_mods.mod_id = 15

尝试:

  1. “items”。* - 只选择您需要的列而不是所有列。

  2. 在以下项目上创建索引:item_mods.item_id AND item_mods.mod_id

  3. 在:items.league_id和items.item_type_id AND num_sockets上创建索引(假设num_sockets在同一个表中)

  4. 任何表现差异?

答案 1 :(得分:1)

这只会缩短并清除语法,但不会发生任何重大变化:

SELECT i.*
FROM   items     i
JOIN   item_mods m ON m.item_id = i.id
WHERE  i.league_id = 1
AND    i.item_type_id = 11
AND    i.num_sockets >= 2
AND    m.mod_id = 15
ORDER  BY m.total_value DESC
LIMIT  25;

这样的查询非常难以进行优化。 Postgres不能只从索引的顶部读取。由于您要按item_mods中的列进行排序,但最具选择性的条件是items,因此很难定制一个可以帮助更多的索引。

当然,您可以优化任一表上的索引。但是如果没有其他信息可以提供给查询,它就不会便宜。 所有符合条件的行必须在Postgres知道获胜者之前阅读。

我们已经在dba.SE上根据这个相关问题开发了解决方案。复杂的东西:
Can spatial index help a “range - order by - limit” query

答案 2 :(得分:0)

以下查询应该提供更好的性能,因为在加入之前已完成过滤。

   SELECT t.* 
   FROM
   (
     SELECT items.* FROM items
     WHERE  (items.league_id = 1) AND (items.item_type_id = 11) 
   ) t 
   INNER JOIN 
   (
      SELECT item_mods.*
      FROM item_mods
      WHERE item_mods.mod_id = 15 
   ) s
   ON s.item_id = t.id 
   WHERE (num_sockets >= 2)
   ORDER BY item_mods.total_value DESC
   LIMIT 25
如果知道它属于哪个表,

num_sockets>=2也可以包含在某个内部查询中。

如果效果更好,请告诉我。