慢PostgreSQL功能

时间:2010-09-07 10:30:32

标签: sql database postgresql

我使用此查询从我的数据库中选择产品。执行此查询需要大约0.220ms。如果删除使用number_percentage()函数的3行,一切都很快(~0.07ms或更短)。

SELECT
 DISTINCT ON (p.id) p.id AS product_id,
 pv.price,
 number_percentage(pv.price,t.percentage) AS price_vat,
 number_percentage(pv.price,pd.size) AS price_discount,
 number_percentage(number_percentage(pv.price,t.percentage),pd.size) AS price_discount_vat

FROM
 product_variant AS pv
INNER JOIN
 product AS p ON p.id=pv.product_id
LEFT JOIN
 product_discount AS pd ON pd.id=pv.product_discount_id
LEFT JOIN
 tax AS t ON t.id=pv.tax_id

ORDER BY
 p.id ASC,
 price_discount ASC

LIMIT 15;

这是number_percentage()函数:

CREATE OR REPLACE FUNCTION number_percentage(n real, p int) RETURNS real AS $$
BEGIN
 IF p IS NULL OR p = 0 THEN
  RETURN n;
 END IF;
 RETURN n * (1 + p / 100);
END;
$$ LANGUAGE plpgsql STABLE;

EXPLAIN ANALYZE(使用ORDER BY price_discount):

Limit  (cost=4371.05..4372.85 rows=15 width=16) (actual time=308.732..308.911 rows=15 loops=1)
  ->  Unique  (cost=4371.05..4389.59 rows=154 width=16) (actual time=308.724..308.843 rows=15 loops=1)
        ->  Sort  (cost=4371.05..4380.32 rows=3709 width=16) (actual time=308.715..308.757 rows=20 loops=1)
              Sort Key: p.id, (number_percentage(pv.price, pd.size))
              Sort Method:  quicksort  Memory: 467kB
              ->  Hash Left Join  (cost=98.12..4151.16 rows=3709 width=16) (actual time=7.473..287.571 rows=4817 loops=1)
                    Hash Cond: (pv.product_discount_id = pd.id)
                    ->  Hash Left Join  (cost=83.62..288.57 rows=3709 width=16) (actual time=7.363..69.976 rows=4817 loops=1)
                          Hash Cond: (pv.tax_id = t.id)
                          ->  Hash Join  (cost=25.47..174.79 rows=3709 width=16) (actual time=7.333..47.134 rows=4817 loops=1)
                                Hash Cond: (pv.product_id = p.id)
                                ->  Seq Scan on product_variant pv  (cost=0.00..94.17 rows=4817 width=16) (actual time=0.019..10.970 rows=4817 loops=1)
                                ->  Hash  (cost=23.54..23.54 rows=154 width=4) (actual time=7.288..7.288 rows=1501 loops=1)
                                      ->  Seq Scan on product p  (cost=0.00..23.54 rows=154 width=4) (actual time=0.013..3.591 rows=1501 loops=1)
                          ->  Hash  (cost=31.40..31.40 rows=2140 width=8) (actual time=0.013..0.013 rows=1 loops=1)
                                ->  Seq Scan on tax t  (cost=0.00..31.40 rows=2140 width=8) (actual time=0.005..0.006 rows=1 loops=1)
                    ->  Hash  (cost=12.00..12.00 rows=200 width=8) (actual time=0.006..0.006 rows=0 loops=1)
                          ->  Seq Scan on product_discount pd  (cost=0.00..12.00 rows=200 width=8) (actual time=0.002..0.002 rows=0 loops=1)
Total runtime: 309.404 ms

EXPLAIN ANALYZE(没有ORDER BY price_discount):

Limit  (cost=4371.05..4372.85 rows=15 width=16) (actual time=285.012..285.187 rows=15 loops=1)
  ->  Unique  (cost=4371.05..4389.59 rows=154 width=16) (actual time=285.004..285.122 rows=15 loops=1)
        ->  Sort  (cost=4371.05..4380.32 rows=3709 width=16) (actual time=284.995..285.036 rows=20 loops=1)
              Sort Key: p.id
              Sort Method:  quicksort  Memory: 467kB
              ->  Hash Left Join  (cost=98.12..4151.16 rows=3709 width=16) (actual time=6.553..270.930 rows=4817 loops=1)
                    Hash Cond: (pv.product_discount_id = pd.id)
                    ->  Hash Left Join  (cost=83.62..288.57 rows=3709 width=16) (actual time=5.720..64.176 rows=4817 loops=1)
                          Hash Cond: (pv.tax_id = t.id)
                          ->  Hash Join  (cost=25.47..174.79 rows=3709 width=16) (actual time=5.693..42.642 rows=4817 loops=1)
                                Hash Cond: (pv.product_id = p.id)
                                ->  Seq Scan on product_variant pv  (cost=0.00..94.17 rows=4817 width=16) (actual time=0.019..10.173 rows=4817 loops=1)
                                ->  Hash  (cost=23.54..23.54 rows=154 width=4) (actual time=5.651..5.651 rows=1501 loops=1)
                                      ->  Seq Scan on product p  (cost=0.00..23.54 rows=154 width=4) (actual time=0.013..2.810 rows=1501 loops=1)
                          ->  Hash  (cost=31.40..31.40 rows=2140 width=8) (actual time=0.012..0.012 rows=1 loops=1)
                                ->  Seq Scan on tax t  (cost=0.00..31.40 rows=2140 width=8) (actual time=0.005..0.005 rows=1 loops=1)
                    ->  Hash  (cost=12.00..12.00 rows=200 width=8) (actual time=0.006..0.006 rows=0 loops=1)
                          ->  Seq Scan on product_discount pd  (cost=0.00..12.00 rows=200 width=8) (actual time=0.001..0.001 rows=0 loops=1)
Total runtime: 285.719 ms

为什么这么慢?功能很简单,所以我不明白这种行为。

3 个答案:

答案 0 :(得分:1)

将功能从STABLE更改为IMMUTABLE。我对8.4.4的测试将解释从每次调用大约0.14ms降低到0.04ms。因为永远不会命中数据库并且实际上是一个真正的函数(即对于每个输入值x,只有一个可能的f(x)),它实际上应该是IMMUTABLE。这应该允许计划者仅为每对(n,p)评估一次。

此外,您的代码中存在错误。这一行:

RETURN n * (1 + p / 100);

应该是这样的:

RETURN n * (1 + p / 100.0);

如上所述,由于 p 是一个整数, p / 100 将被视为整数除法,给出不正确的结果(至少从我对这个函数的假设出发)应该是这样做的。

答案 1 :(得分:0)

狂野猜测:ORDER BY price_discount是问题所在。数据库必须先处理所有记录,然后才能执行任何排序操作并吐出请求的15个结果。

您是否可以向我们展示使用和不使用ORDER BY的EXPLAIN ANALYZE的结果?

答案 2 :(得分:0)

如果您在pg admin 3中运行explain query,您将能够看到查询是如何分解的。那你可以

有时添加索引会解决您的问题,其他时候会尝试重写查询。您可以尝试使用CTE,但因为您的功能似乎是一个问题reading this may help