2010-09-07 4 views
2

J'utilise cette requête pour sélectionner des produits de ma base de données. L'exécution de cette requête prend ~ 0.220ms. Si je supprime les 3 lignes où la fonction number_percentage() est utilisée, tout est rapide (~ 0.07ms ou moins).Fonction PostgreSQL lente

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; 

Ceci est la fonction de 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 (avec 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 (sans 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 

W Pourquoi est-ce si lent? La fonction est simple, donc je ne comprends pas ce comportement.

Répondre

0

Wild deviner: L'ORDER BY price_discount est le problème. La base de données doit traiter tous les enregistrements avant de pouvoir effectuer une opération de tri et cracher les 15 résultats demandés.

Pourriez-vous nous montrer les résultats de EXPLAIN ANALYZE avec et sans ORDER BY?

+0

J'ai mis à jour la réponse, mais ORDER BY n'est pas le problème. Ce sont ces 3 fonctions number_percentage(). Si elles sont supprimées, la requête est rapide. – Paul

+0

Pourrait également montrer utiliser l'ANALAYZE EXPLAIN (y compris ANALYZE!) De la requête sans l'appel de fonction? Et avez-vous indexé les colonnes pv.product_discount_id, pd.id, pv.tax_id, t.id, pd.id et pv.product_discount_id? –

+0

J'ai mis à jour la réponse à nouveau (avec EXPLAIN ANALYZE). – Paul

0

Si vous exécutez une requête d'explication dans pg admin 3, vous pourrez voir comment la requête est décomposée. Alors vous pouvez

parfois en ajoutant un index résoudra votre problème, d'autres fois essayez de réécrire la requête. Vous pourriez essayer d'utiliser un CTE, mais comme votre fonction semble être un problème reading this may help.

1

Modifiez la fonction de STABLE à IMMUTABLE. Mes tests sur 8.4.4 ont laissé tomber l'explication d'environ 0.14 ms à 0.04 ms par appel. Puisqu'il jamais frappe la base de données et est vraiment une vraie fonction (c'est-à-dire pour chaque valeur d'entrée x, il y en a une et une seule possible f (x)), elle devrait vraiment être IMMUTABLE. Cela devrait permettre au planificateur de l'évaluer une seule fois pour chaque paire de (n, p).

De plus, il y a un bug dans votre code. Cette ligne:

RETURN n * (1 + p/100); 

devrait être ceci:

RETURN n * (1 + p/100.0); 

Comme écrit, depuis p est un entier, p/100 sera evaulated comme division entière, ce qui donne un résultat incorrect (à moins de mon hypothèse de ce que cette fonction est supposée faire).

+0

En regardant à nouveau, vous ne pouvez vraiment pas supprimer la commande ORDER BY price_discount puisque vous utilisez DISTINCT ON (p.id). Si vous supprimez cela, l'enregistrement "premier" par p.id n'est pas défini et vous ne récupérerez pas toujours le même résultat sur différentes exécutions de la requête. Vous devez avoir un ORDER BY secondaire comme ça afin de vous assurer de retrouver le même enregistrement à chaque fois. –

+0

Merci!Tu m'as corrigé un bug, mais la performance est toujours très mauvaise. – Paul

+0

Pouvez-vous mettre à jour votre question avec une analyse d'explication avec le correctif de bogue et le changement IMMUTABLE en place? –

Questions connexes