【问题标题】:PostgreSQL simple query optimizationPostgreSQL 简单查询优化
【发布时间】:2014-07-06 12:47:26
【问题描述】:

PostgreSQL 8.4;三张表 - store (~100k, pk id, fk supplier_id & item_id), supplier(~10 pk supplier_id), item(~1000 pk item_id);

我创建了以下查询来获取我需要的数据:

SELECT store.quantity, store.price, x.supplier_name
FROM store NATURAL JOIN
     (SELECT * FROM item NATURAL JOIN supplier) AS x 
 WHERE store.price > 500 AND store.quantity > 0 AND
       store.quantity < 100 AND
       x.item_name = 'SomeName';

查询计划:

Nested Loop  (cost=20.76..6513.55 rows=8 width=229)
  ->  Hash Join  (cost=20.76..6511.30 rows=8 width=15)
        Hash Cond: (store.item_id = item.item_id)
        ->  Seq Scan on store  (cost=0.00..6459.00 rows=8388 width=23)
              Filter: ((price > 500::numeric) AND (quantity > 0) AND (quantity < 100))
        ->  Hash  (cost=20.75..20.75 rows=1 width=8)
              ->  Seq Scan on item  (cost=0.00..20.75 rows=1 width=8)
                    Filter: ((item_name)::text = 'SomeName'::text)
  ->  Index Scan using supplier_pkey on supplier  (cost=0.00..0.27 rows=1 width=222)
        Index Cond: (supplier.supplier_id = store.supplier_id)

现在的目标是通过优化查询本身将成本降低 30% 以上。我发现的这个问题的唯一实例是通过修改表或服务器设置来解决的,但我希望通过修改查询来解决这个问题,这就是我研究不足的地方。

显然要解决的问题是 Seq Scan,这让我想到我需要安排它,以便扫描/过滤仅应用于存储表的子集 - 但 iirc 你需要扫描表任何这种情况,所以也许使用 Seq Scan 以外的东西?索引扫描无济于事,因为我不会按索引进行过滤...我在这里感到困惑,因为这似乎更像是 PostgreSQL 优化器做出的选择,而不是我可以随意更改的东西...

(如果您想知道,这是作业的一部分,我在这里问是因为我花了好几个小时研究这个问题,但没有找到任何相关的东西,我就放弃了,但我'我还是很好奇...)

【问题讨论】:

    标签: sql postgresql query-optimization


    【解决方案1】:

    你可以用索引来解决这个问题。由于“自然连接”,很难说出键是什么。 (我推荐using 而不是natural join,这样您至少可以看到正在使用哪些键,并且如果其中一个表被修改,它不会弄乱连接。)

    我认为item(item_name, item_id) 上的索引将有助于查询计划。

    【讨论】:

    • 这样做只会将项目的哈希值从 20.75 提高到 8.27,这与 6000ish seq 扫描相比微不足道。 (我将连接使用的相关索引添加到操作中)
    • 我在发布问题时没有考虑全局更改,但您的建议确实有效 - 要添加的正确索引是 store(item_id,price),这导致查询计划非常不同并减少了成本一直是原件的 5% :-) (不过,我并没有将您的答案标记为已接受,因为我仍然对是否可以在本地进行优化感兴趣 - 无论如何操作人员都这么说)
    • @user742925 。 . .您可以提示查询使用基于散列的算法进行连接,而不是嵌套循环。我的猜测是这可能是性能瓶颈所在。
    【解决方案2】:

    会很难优化,因为它看起来不错,试试这个来避免子查询:

    SELECT 
        store.quantity, 
        store.price, 
        supplier.supplier_name 
    FROM store 
        INNER JOIN item
            ON store.item_id = item.item_id
        INNER JOIN supplier
            ON supplier.supplier_id = store.supplier_id
            AND supplier.item_name = 'SomeName'
    WHERE 
        store.price > 500 
        AND store.quantity BETWEEN 0 AND 100;
    

    使用BETWEEN 更好。

    另外,添加索引

    • store.item_id
    • item.item_id
    • supplier.item_name

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-13
      • 2022-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-12
      相关资源
      最近更新 更多