PostgreSQL La base de donnees la plus sophistiquee au monde.

Forums PostgreSQL.fr

Le forum officiel de la communauté francophone de PostgreSQL

Vous n'êtes pas identifié(e).

#1 03/08/2012 14:51:47

damalaan
Membre

Recherche d'optimisation sur calcul de requete

Bonjour

j'ai la requete suivante basée sur 2 table (en 1-n) dont la structure est également ci-dessous
la requête :

CREATE OR REPLACE VIEW view_ecart AS 
 SELECT tbl_tournee_trn.trn_esp, tbl_tournee_trn.trn_date_prel, tbl_tournee_trn.trn_tournee, tbl_tournee_trn.trn_chauffeur, tbl_tournee_trn.trn_usine, tbl_valeur_mg.mg_matricule, tbl_valeur_mg.mg_val, 
        CASE lag(tbl_valeur_mg.mg_val, 1) OVER (PARTITION BY tbl_valeur_mg.mg_matricule ORDER BY tbl_tournee_trn.trn_esp, tbl_tournee_trn.trn_date_prel)
            WHEN 0 THEN NULL::numeric
            ELSE 
            CASE tbl_valeur_mg.mg_val
                WHEN 0 THEN NULL::numeric
                ELSE tbl_valeur_mg.mg_val - lag(tbl_valeur_mg.mg_val, 1) OVER (PARTITION BY tbl_valeur_mg.mg_matricule ORDER BY tbl_tournee_trn.trn_esp, tbl_tournee_trn.trn_date_prel)
            END
        END AS ecart
   FROM tbl_valeur_mg
   JOIN tbl_tournee_trn USING (trn_id)
  WHERE tbl_tournee_trn.trn_date_prel > 100000;

ALTER TABLE view_ecart OWNER TO postgres;

table du coté 1:

CREATE TABLE tbl_tournee_trn
(
  trn_date_prel integer NOT NULL,
  trn_esp integer NOT NULL,
  trn_tournee integer NOT NULL,
  trn_usine integer NOT NULL,
  trn_id bigserial NOT NULL,
  trn_chauffeur integer,
  CONSTRAINT "clé_primaire" PRIMARY KEY (trn_id),
  CONSTRAINT "clé_2" UNIQUE (trn_date_prel, trn_esp, trn_tournee, trn_usine)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE tbl_tournee_trn OWNER TO postgres;

table du coté n

CREATE TABLE tbl_valeur_mg
(
  mg_id bigserial NOT NULL,
  trn_id bigserial NOT NULL,
  mg_matricule integer NOT NULL,
  mg_val numeric(4,1) NOT NULL,
  CONSTRAINT pk PRIMARY KEY (mg_id),
  CONSTRAINT fk FOREIGN KEY (trn_id)
      REFERENCES tbl_tournee_trn (trn_id) MATCH FULL
      ON UPDATE CASCADE ON DELETE CASCADE
)
WITH (
  OIDS=FALSE
);
ALTER TABLE tbl_valeur_mg OWNER TO postgres;

-- Index: index_mg

-- DROP INDEX index_mg;

CREATE INDEX index_mg
  ON tbl_valeur_mg
  USING btree
  (trn_id, mg_matricule);

Il y a un index de positionner, mais l'est-il bien?
la requete met une 30aine de seconde pour renvoyer 500000 lignes

je fais souvent appel à cette requête car je travaille beaucoup sur le calcul qui en est issu et je recherche un moyen d'augmenter un peu la rapidité

merci d'avance

Hors ligne

#2 03/08/2012 15:11:56

rjuju
Administrateur

Re : Recherche d'optimisation sur calcul de requete

Bonjour,

un explain analyze de la requête aiderait à vous aider, mais à première vue, un index sur tbl_tournee_trn.trn_date_prel pourrait être utile selon la taille de votre table.

Hors ligne

#3 03/08/2012 15:17:08

damalaan
Membre

Re : Recherche d'optimisation sur calcul de requete

taille table tbl_tournee_trn : Lignes estimées    73145   
taille table tbl_valeur_mg : Lignes estimées    650283   

voilà pour le explain

"WindowAgg  (cost=130671.82..151806.02 rows=650283 width=32) (actual time=2739.974..4027.162 rows=496831 loops=1)"
"  ->  Sort  (cost=130671.82..132297.53 rows=650283 width=32) (actual time=2739.920..3132.560 rows=496831 loops=1)"
"        Sort Key: tbl_valeur_mg.mg_matricule, tbl_tournee_trn.trn_esp, tbl_tournee_trn.trn_date_prel"
"        Sort Method:  external merge  Disk: 19624kB"
"        ->  Hash Join  (cost=2787.50..36762.70 rows=650283 width=32) (actual time=181.034..1570.746 rows=496831 loops=1)"
"              Hash Cond: (tbl_valeur_mg.trn_id = tbl_tournee_trn.trn_id)"
"              ->  Seq Scan on tbl_valeur_mg  (cost=0.00..11284.83 rows=650283 width=20) (actual time=0.052..238.358 rows=650283 loops=1)"
"              ->  Hash  (cost=1551.74..1551.74 rows=63901 width=28) (actual time=58.872..58.872 rows=62807 loops=1)"
"                    Buckets: 2048  Batches: 4  Memory Usage: 794kB"
"                    ->  Seq Scan on tbl_tournee_trn  (cost=0.00..1551.74 rows=63901 width=28) (actual time=2.371..28.166 rows=62807 loops=1)"
"                          Filter: (trn_date_prel > 100000)"
"Total runtime: 4120.821 ms"

Hors ligne

#4 03/08/2012 15:33:50

rjuju
Administrateur

Re : Recherche d'optimisation sur calcul de requete

Si vous pouvez vous le permettre, vous pourriez dans un premier temps allouer un peu plus de ram à votre connexion pour éviter un tri sur disque, ce qui devrait faire gagner un peu de temps.
Essayez:

SET work_mem TO '40MB';
EXPLAIN ANALYZE SELECT ...

Hors ligne

#5 06/08/2012 09:32:05

gleu
Administrateur

Re : Recherche d'optimisation sur calcul de requete

Comme le dit Julien, la durée de la requête dépend principalement de la durée du tri (opération Sort). Elle prend 1,5 secondes à elle seule. La raison vient certainement du fait qu'une partie du tri doit se faire sur disque (20 Mo écrit). Du coup, augmenter work_mem permettrait de gagner sur ce temps de tri.

Vous parliez d'un index au début. Il est clair que la requête ne passe pas par un index. Elle fait un parcours séquentiel de la table tbl_tournee_trn et filtre en même temps sur la colonne trn_date_prel. Coller un index sur cette colonne pourrait faire qu'il utilise un index mais de toute façon, le parcours séquentiel est très rapide (28 ms), donc ça ne permettra pas de gagner grand chose sur une requête qui dure 4 secondes.

Les deux opérations qui prennent du temps sont la jointure (1,2 seconde) et le tri (1,5 secondes). Les statistiques sont plutôt précises, il y a donc peu de chances de gagner autrement qu'en augmentant work_mem. Par contre, je pense qu'il faudra aller un peu au-dessus de 40 Mo.


Guillaume.

Hors ligne

#6 08/08/2012 11:20:23

damalaan
Membre

Re : Recherche d'optimisation sur calcul de requete

Effectivement en plaçant un index sur la colonne trn_date_prel, et en augmentant la mémoire (50MB), je gagne pas grand chose :  1 à 2 s.

Merci pour ces infos.

Hors ligne

#7 08/08/2012 12:05:42

gleu
Administrateur

Re : Recherche d'optimisation sur calcul de requete

Si je puis me permettre, vous divisez quand même la durée d'exécution par deux.


Guillaume.

Hors ligne

#8 09/08/2012 16:33:47

damalaan
Membre

Re : Recherche d'optimisation sur calcul de requete

Oui mais au global ça dure toujours une trentaine de sec!

Je pense à autre chose : cette fameuse vue me sert à 4 autres en cascade(la vue 1 sert à la vue2, la vue 2 sert à la vue 3 etc), et dès la première je renvoie 500000lignes avec le

WHERE tbl_tournee_trn.trn_date_prel > 100000

Or je n'ai que très rarement besoin de travailler sur tout ça. Il me faut juste des stats sur 3 mois, donc par exemple pour Aout 2012, il faudrait que je récupère juste les données depuis juin 2012

il me faudrait qqc du genre

WHERE to_timestamp(tbl_tournee_trn.trn_date_prel/100, 'yymm') between Moncritère and Moncritère-'3 Month'::interval)

Il faudrait que je puisse paramétrer ce critère à chaque fois,.............ce que je ne sais pas faire

et pour avoir tester 'à la main', j'en suis à 2 à 3s d'exécution ce qui nickel pour l'utilisateur

Hors ligne

#9 09/08/2012 16:45:58

gleu
Administrateur

Re : Recherche d'optimisation sur calcul de requete

Je vois 4 secondes sur votre dernier EXPLAIN.

Concernant le coup des vues, il est clair que passer par des groupes de vues qui récupèrent plus d'infos que nécessaire peut être un gros soucis. Il peut être intéressant de ne pas passer par les vues pour construire une requête plus simple à exécuter.


Guillaume.

Hors ligne

#10 10/08/2012 08:12:50

damalaan
Membre

Re : Recherche d'optimisation sur calcul de requete

Je crois n'avoir pas bien compris le Explain : effectivement je suis à 2.5s d'exécution après les modifs d'index et de mémoire.
mais le select en lui même n'a pas tellement changé : pg Admin renvoie

Je voudrais donc poursuivre sur mon idée d'hier de n'utiliser que les éléments nécessaires et non toute une table.

Faut-il que je crée une fonction qui fasse toutes requêtes d'un seul coup en les supprimant et recréant à chaque fois?

Hors ligne

#11 10/08/2012 09:33:18

gleu
Administrateur

Re : Recherche d'optimisation sur calcul de requete

Supprimer et recréer des requêtes ? désolé, mais ça n'a aucun sens.


Guillaume.

Hors ligne

#12 16/08/2012 16:25:52

damalaan
Membre

Re : Recherche d'optimisation sur calcul de requete

je suis bien d'accord que supprimer des requêtes puis les recréer n'est pas très ....pro

Le principe que j'ai actuellement :
R1 : calcul des écarts entre les valeurs d'une même colonne en utilisant une fonction WINDOW
R2 : calcul à partir de l'écart calculé en R1 un écart type, avec un regroupement sur 4 colonnes (espèce, usine, tournée, mois)
R3 : calcul à partir de  l'écart-type calculé en R2, la moyenne des écart types avec un regroupement sur 2 colonnes (espèce, mois)
R4 : comparaison des écart types obtenus en R2 avec la moyenne R3 pour définir des alertes
R5 : comparaison des alertes identifiées en R4 sur 2 mois consécutifs, afin d'identifier la récurrence éventuelle d'apparition des alertes

J'ai commencé à essayer de rassembler tous mes requêtes en 1 seule mais ça devient rapidement compliqué! J'en suis resté à R2!

J'imagine donc qu'il va falloir que je poursuive mon idée en PL/pgSQL. Est-il possible de créer un genre de requête temporaire en PL/pgSQL? (et est-ce une bonne idée?)

Hors ligne

#13 16/08/2012 22:26:52

rjuju
Administrateur

Re : Recherche d'optimisation sur calcul de requete

S'il ne s'agit que d'aider la lisibilité du code et d'éviter des imbrications énormes, vous pouvez vous servir de CTE. ex de syntaxe:

WITH r1 AS (SELECT --calcul des écart avec fonction window
   WHERE to_timestamp ...),
r2 AS (SELECT ... FROM r1),
r3 AS (SELECT ... FROM r2)

SELECT * FROM r3 WHERE ....

Hors ligne

#14 17/08/2012 08:28:58

damalaan
Membre

Re : Recherche d'optimisation sur calcul de requete

NICKEL!!!

Avec le même volume de données (soit plus de 530 000 lignes), je gagne 20s d'exécution (je passe de 36s à 17s)
Tout en sachant que je ne travaille la plupart du temps que sur 50 000 à 70 000 lignes, la requête finale prend alors moins d'1s. cool

Avec tous mes remerciements !

Hors ligne

Pied de page des forums