sql - 缓慢的 PostgreSQL 函数

标签 sql database postgresql

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

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

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

最佳答案

将函数从 STABLE 更改为 IMMUTABLE。我在 8.4.4 上的测试将每次调用的解释时间从大约 0.14 毫秒减少到 0.04 毫秒。由于它从不 访问数据库并且确实是一个真正的函数(即对于每个输入值 x,只有一个可能的 f(x)),它确实应该是不可变的。这应该允许规划者只对每对 (n,p) 进行一次评估。

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

RETURN n * (1 + p / 100);

应该是这样的:

RETURN n * (1 + p / 100.0);

正如所写,因为 p 是一个整数,p/100 将被计算为整数除法,给出不正确的结果(至少根据我对这个函数的假设应该做的)。

关于sql - 缓慢的 PostgreSQL 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3657797/

相关文章:

sql - PostgreSQL - 使用 INHERITS 而不是引用来建模 OOP 继承的优点和缺点?

mysql - 将记录转置为 mysql 中 3 个表的记录

postgresql - 如何在触发器函数中将 NEW.* 传递给 EXECUTE

postgresql - postgresql 中的时间旅行函数

sql - 在 SQL Server 2008 中运行查询时是否需要包含方括号 []?

sql - 使用for循环在oracle sql中更新多行

mysql - 无法在ubuntu上使用tomcat6连接到mysql数据库

java - 修改 JDBC 代码以针对 Inesrt/Update/Deletions 的多个数据库服务器

hibernate - 带有 PostgreSQL 的 Grails 不生成表

sql - 比较数据库中一列存在但另一列不存在的记录