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

#1 10/01/2011 16:35:34

gilou974
Membre

Fonctions imbriquées -- optimisation

Bonjour à tous et meilleurs voeux pour 2011 ......

Voilà, je dois réaliser des écritures d'à nouveaux et pour ce faire je dispose de quatres fonction comme suit

1) Celle en entrée qui donne tous les societaires consernés :

CREATE OR REPLACE FUNCTION parcours_plancomptable(annee integer, idutilisateur integer)
  RETURNS integer AS
$BODY$

DECLARE
    nbLigne integer;                -- nombre de lignes crées pour un plan comptable
    nbLigneTotal integer :=0;            -- nombre de lignes totales crées pour l'execution du script
    soctaire RECORD;                -- Ligne contenant un societaire
    idNaturePiece integer := 9;            -- id nature piece : A nouveau
BEGIN
     -- Création pîece comptable --
    INSERT INTO piece_comptable(id_pct, id_npc, bl_valide_pct)
        VALUES (nextval('piece_comptable_id_seq'), idNaturePiece, false);
       
    FOR soctaire IN (
        SELECT distinct soc.id_plc FROM societaire soc
            JOIN ligne_ecriture le ON soc.id_plc = le.id_plc
            WHERE st_etat_soc IN ('SP', 'DO', 'EX') AND nm_soc_soc <10000
            AND EXTRACT(YEAR FROM le.d_dateecriture_lec) = annee - 1 AND id_grp not between 36 and 51
            ORDER BY soc.id_plc ASC)
    LOOP
        EXECUTE 'SELECT parcours_groupe(' || soctaire.id_plc || ', currval(''piece_comptable_id_seq''), ' || annee || ', ' || idUtilisateur || ')' INTO nbLigne;
        nbLigneTotal := nbLigneTotal + nbLigne;
    END LOOP;
    RETURN nbLigneTotal;
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE
  COST 100;
ALTER FUNCTION parcours_plancomptable(integer, integer) OWNER TO postgres;

2 ) une pour les groupes comptables

CREATE OR REPLACE FUNCTION parcours_groupe(idplancomptable integer, idpiececomptable bigint, annee integer, idutilisateur integer)
  RETURNS integer AS
$BODY$
DECLARE
    solde numeric(18,2) := 0.00;         -- variable récupéré de la fonction solde_groupe()
    grp RECORD;                -- Ligne contenant un groupe pour tout les groupes sauf (7,8,9,10,11,12)
    ligneEcriture RECORD;            -- Ligne contenant un groupe uniquement pour les groupes (7,8,9,10,11,12)
    cotis RECORD;                -- Ligne contenant un groupe uniquement pour les groupes (22,23,24,25,26,27)
    txt text :='';                -- log
    dateEcriture date;            -- Date avec annee courante
    lbl text :='';                -- libelle de la ligne ecriture
    debit numeric(18,2) := 0.00;        -- Debit pour la ligne écriture
    credit numeric(18,2) := 0.00;        -- Debit pour la ligne ecriture
    nbLigne integer :=0;            -- Compte le nombre de ligne crée
    idJournal integer := 3;            -- Identifiant du journal : A NOUVEAU
    anneeN1 integer := 0;            -- Annee + 1
    anneeSolde integer := 0;        -- Annee - 1
    trancheGar integer := 0;        -- Tranche garantie
    idJournalCotis integer := 16;        -- Identifiant du journal : COTISATION EXERCICE
    lblCotisation text := '';        -- Libelle de la ligne écriture cotisation
    soldeCotis numeric(18,2) := 0.00;    -- Solde d'un des groupes COTIS_
    debitCotis numeric(18,2) := 0.00;    -- Debit d'un des groupes COTIS_
    creditCotis numeric(18,2) := 0.00;    -- Credit d'un des groupes COTIS_
    dateEcritureCotis date;            -- Date pour les lignes écriture des groupes COTIS_
    groupeCotis integer := 66;        -- Groupe COMPTABILITE GENERALE
    totalCotis numeric(18,2) := 0.00;    -- Total de tout les soldes groupe des cotisations
BEGIN   
    -- Déclaration des variables --
    anneeN1 = annee + 1;                            -- Annee + 1
    anneeSolde = annee - 1;                            -- Annee -1
    dateEcriture := to_date(annee || '-01-01', 'YYYY-MM-DD');         -- Date au 01/01/anneeCourante
    lbl := 'A NOUVEAU AU 1/1/' || annee;                    -- Creation libelle
    lblCotisation := 'COTISATION EXERCICE ' || anneeSolde;            -- Création libelle cotisation
    dateEcritureCotis := to_date(anneeSolde || '-12-31', 'YYYY-MM-DD');    -- Date au 31/12/anneeCourante -1

    -- Génération des lignes écritures --
    --execute la fonction solde_groupe pour tous les groupes hormis ceux de fdg, de HB et de cotis ainsi que les fonds restituables
    FOR grp IN (SELECT g.id_grp
        FROM groupe g
        WHERE g.id_grp NOT IN (7,8,9,10,11,12,13,22,23,24,25,26,27)     -- 7 => FDG_S - 8 => FDG_T - 9 => FDG_G - 10 => FDG_P - 11 => FDG_M - 12 => FDG_NP -
        AND g.id_grp NOT BETWEEN 36 AND 51 )                                -- 13 => FondsRest - 22 => COTIS_S - 23 => COTIS_T - 24 => COTIS_G - 25 => COTIS_P -
                                        -- 26 => COTIS_M - 27 => COTIS_EX_NC
    LOOP
        EXECUTE 'SELECT solde_groupe(' || grp.id_grp || ', ' || anneeSolde || ', ' || idPlanComptable || ')' INTO solde;
       
        IF (solde<>0 OR solde <> NULL) THEN                    -- Si solde != 0 alors injecte une ligne
           
            IF (solde > 0) THEN
                debit := solde;
                credit := 0.00;
            ELSE
                debit := 0.00;
                credit := ABS(solde);
            END IF;
           
            INSERT INTO ligne_ecriture(
                id_lec, id_pct, id_grp, id_plc, id_jnx, id_bqe, d_dateecriture_lec,
                st_lbl_lec, nm_debit_lec, nm_credit_lec, d_datesaisie_lec, nm_tranchegar_lec,
                st_anneegar_lec, nm_numremise_lec, nm_numcheque_lec, id_usr,
                bl_transfere_lec)
            VALUES (nextval('ligne_ecriture_id_seq'), idPieceComptable, grp.id_grp, idPlanComptable, idJournal, null, dateEcriture,
                lbl, debit, credit, NOW(), 0,
                annee, null, null, idUtilisateur,
                false);
               
            nbLigne    := nbLigne + 1;
            --RAISE NOTICE 'Log pour le groupe %  avec le plan comptable % (solde %) ', grp.id_grp, idPlanComptable, solde;
        END IF;
    END LOOP;
   
    -- Pour chaque ligne ecriture dont l'année correspond à l'année courante et au groupe de Fonds de Garantie
   
    FOR ligneEcriture IN (SELECT le.id_grp, le.nm_debit_lec, le.nm_credit_lec, le.nm_tranchegar_lec, st_anneegar_lec FROM ligne_ecriture le
        WHERE (EXTRACT(YEAR FROM le.d_dateecriture_lec)) = '2010'
        AND le.id_plc = idPlanComptable
        AND le.id_grp IN (7,8,9,10,11,12)) -- 7 => FDG_S - 8 => FDG_T - 9 => FDG_G - 10 => FDG_P - 11 => FDG_M - 12 => FDG_NP
    LOOP
    EXECUTE 'SELECT solde_groupe(' || grp.id_grp || ', ' || anneeSolde || ', ' || idPlanComptable || ')' INTO solde;
        IF (solde<>0 OR solde <> NULL) THEN
            IF (ligneEcriture.nm_tranchegar_lec <> null) THEN
                trancheGar := ligneEcriture.nm_tranchegar_lec;
            ELSE
                trancheGar := 0;
            END IF;
           
            INSERT INTO ligne_ecriture(
                id_lec, id_pct, id_grp, id_plc, id_jnx, id_bqe, d_dateecriture_lec,
                st_lbl_lec, nm_debit_lec, nm_credit_lec, d_datesaisie_lec, nm_tranchegar_lec,
                st_anneegar_lec, nm_numremise_lec, nm_numcheque_lec, id_usr,
                bl_transfere_lec)
            VALUES (nextval('ligne_ecriture_id_seq'), idPieceComptable, ligneEcriture.id_grp, idPlanComptable, idJournal, null, dateEcriture,
                lbl, ligneEcriture.nm_debit_lec, ligneEcriture.nm_credit_lec, NOW(), trancheGar,
                ligneEcriture.st_anneegar_lec, null, null, idUtilisateur,
                false);
               
            nbLigne    := nbLigne + 1;
            --RAISE NOTICE 'Log pour le groupe %  avec le plan comptable % (solde %): %', grp.id_grp, idPlanComptable, solde, txt;
        END IF;
    END LOOP;

    -- On calcul le solde total pour les groupes cotisation. Si le solde est négatif erreur
    EXECUTE 'SELECT solde_groupeCotis(' || idPlanComptable || ', '|| annee ||')' INTO totalCotis;
    IF (totalCotis > 0) THEN
        RAISE EXCEPTION 'ERREUR la somme des soldes groupes des cotisations est négatif';
        RETURN nbLigne;
    END IF;
   
    -- Pour les groupes de cotisation : COTIS_S, COTIS_T, COTIS_G, COTIS_P, COTIS_M, COTIS_EX_NC
    FOR cotis IN (SELECT le.id_grp, le.nm_debit_lec, le.nm_credit_lec, le.nm_tranchegar_lec, st_anneegar_lec FROM ligne_ecriture le
        WHERE (EXTRACT(YEAR FROM le.d_dateecriture_lec)) = annee
        AND le.id_plc = idPlanComptable
        AND le.id_grp IN (22,23,24,25,26,27))         -- 22 => COTIS_S - 23 => COTIS_T - 24 => COTIS_G - 25 => COTIS_P - 26 => COTIS_M - 27 => COTIS_EX_NC
    LOOP
        EXECUTE 'SELECT solde_groupe(' || cotis.id_grp || ', ' || anneeSolde || ', ' || idPlanComptable || ')' INTO soldeCotis;

        IF (soldeCotis < 0) THEN
            debitCotis := soldeCotis;
            creditCotis := 0.00;
        ELSE
            debitCotis := 0.00;
            creditCotis := ABS(soldeCotis);
        END IF;
       
        INSERT INTO ligne_ecriture(
            id_lec, id_pct, id_grp, id_plc, id_jnx, id_bqe, d_dateecriture_lec,
            st_lbl_lec, nm_debit_lec, nm_credit_lec, d_datesaisie_lec, nm_tranchegar_lec,
            st_anneegar_lec, nm_numremise_lec, nm_numcheque_lec, id_usr,
            bl_transfere_lec)
        VALUES (nextval('ligne_ecriture_id_seq'), idPieceComptable, cotis.id_grp, idPlanComptable, idJournalCotis, null, dateEcritureCotis,
            lblCotisation, debitCotis, creditCotis, NOW(), 0,
            anneeSolde, null, null, idUtilisateur,
            false);
           
        nbLigne    := nbLigne + 1;
       
        -- Contre partie   
        INSERT INTO ligne_ecriture(
            id_lec, id_pct, id_grp, id_plc, id_jnx, id_bqe, d_dateecriture_lec,
            st_lbl_lec, nm_debit_lec, nm_credit_lec, d_datesaisie_lec, nm_tranchegar_lec,
            st_anneegar_lec, nm_numremise_lec, nm_numcheque_lec, id_usr,
            bl_transfere_lec)
        VALUES (nextval('ligne_ecriture_id_seq'), idPieceComptable, groupeCotis, idPlanComptable, idJournalCotis, null, dateEcritureCotis,
            lblCotisation, 0.00, totalCotis, NOW(), 0,
            anneeSolde, null, null, idUtilisateur,
            false);
           
        nbLigne    := nbLigne + 1;
    END LOOP;
    RETURN nbLigne;
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE
  COST 100;
ALTER FUNCTION parcours_groupe(integer, bigint, integer, integer) OWNER TO postgres;

3 ) une autre pour le solde des groupes permettant de ne générer des ecritures que pour les groupes avec un solde non null et différent de 0

CREATE OR REPLACE FUNCTION solde_groupe(idgroupe integer, annee integer, idplancomptable integer)
  RETURNS numeric AS
$BODY$

DECLARE
    solde numeric(18,2) := 0.00;
    ecriture RECORD;
BEGIN
    SELECT sum(le.nm_debit_lec) as sDebit, sum(le.nm_credit_lec) as sCredit
        INTO ecriture FROM ligne_ecriture le
        WHERE le.id_grp = idGroupe
        AND le.id_plc = idPlanComptable
        AND EXTRACT(YEAR FROM le.d_dateecriture_lec) = annee;
    solde = ecriture.sDebit -ecriture.sCredit;                -- Calcule SOMME(credit) - SOMME(debit)
    --RAISE NOTICE 'solde = % ', solde;
    IF (solde IS NOT NULL) THEN
        RETURN solde;
    ELSE
        RETURN 0.00;
    END IF;
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE
  COST 100;
ALTER FUNCTION solde_groupe(integer, integer, integer) OWNER TO postgres;

4) une dernière spécifique à une catégorie de groupe comptable

CREATE OR REPLACE FUNCTION solde_groupecotis(idplancomptable integer, annee integer)
  RETURNS numeric AS
$BODY$

DECLARE
    soldeGroupe numeric(18,2) := 0.00;             -- Solde du groupe
    soldeTotal numeric(18,2) := 0.00;            -- Solde total de tout les groupes
    anneeSolde integer := 0;                -- Annee - 1
    cotis RECORD;
BEGIN
    anneeSolde := annee - 1;
    FOR cotis IN (SELECT le.id_grp, le.nm_debit_lec, le.nm_credit_lec, le.nm_tranchegar_lec, st_anneegar_lec FROM ligne_ecriture le
        WHERE (EXTRACT(YEAR FROM le.d_dateecriture_lec)) = anneeSolde
        AND le.id_plc = idPlanComptable
        AND le.id_grp IN (22,23,24,25,26,27))         -- 22 => COTIS_S - 23 => COTIS_T - 24 => COTIS_G - 25 => COTIS_P - 26 => COTIS_M - 27 => COTIS_EX_NC
    LOOP
        EXECUTE 'SELECT solde_groupe(' || cotis.id_grp || ', ' || anneeSolde || ', ' || idPlanComptable || ')' INTO soldeGroupe;
        soldeTotal := soldeTotal + soldeGroupe;
    END LOOP;
    RETURN soldeTotal;
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE
  COST 100;
ALTER FUNCTION solde_groupecotis(integer, integer) OWNER TO postgres;


VOilà. Mon problème, vous aurez compris vu le sujet serait de pouvoir optimiser le traitement.

Pour 7770 societaire concernés et 339494 lignes dans ma table d'écriture, mon traitement n'est toujours pas terminé après plus de trois heures.

Pourriez - vous me donner des astuces notement sur le fait d'apeler les fonctions à l'intérieur d'autres fonctions et ce que çà coute en tps et me dire si le mieux ne serait pas de faire tout le traitement dans une seule fonction.


Merci bcp à tous et encore plus à ceux qui pourraient m'aider.

Hors ligne

#2 10/01/2011 17:35:20

gleu
Administrateur

Re : Fonctions imbriquées -- optimisation

Vu qu'il s'agit simplement de calcul, le mieux serait d'écrire la procédure en C. C'est là que vous aurez les meilleures performances.

Quant à améliorer les procédures stockées, oui, c'est possible. Prenez la dernière par exemple. Vous pouvez la faire en une seule instruction SQL, ce qui devrait être bien plus rapide.

SELECT sum(solde_groupe(le.id_grp, annee, idPlanComptable))
FROM ligne_ecriture le
WHERE (EXTRACT(YEAR FROM le.d_dateecriture_lec)) = annee -1
  AND le.id_plc = idPlanComptable
  AND le.id_grp IN (22,23,24,25,26,27)

Du coup, cela devient une procédure SQL, ce qui permet encore de grapiller un peu.

Bref, évitez de récupérer des colonnes que vous n'utilisez pas, évitez les boucles inutiles, etc. Utilisez des procédures SQL plutôt que PL/pgsql lorsque c'est possible.


Guillaume.

Hors ligne

#3 10/01/2011 17:39:56

gilou974
Membre

Re : Fonctions imbriquées -- optimisation

Tout d'abord merci à vous,

on est donc d'accord mieux vaut une procédure très longue que plusieures petites fonctions dès lors que l'on peut tout réaliser en sql...

Merci encore

Hors ligne

#4 10/01/2011 18:30:56

gleu
Administrateur

Re : Fonctions imbriquées -- optimisation

Non, je n'ai pas dit ça. Mieux vaut une grosse REQUETE qu'une petite fonction, là, oui.


Guillaume.

Hors ligne

#5 10/01/2011 18:56:15

gilou974
Membre

Re : Fonctions imbriquées -- optimisation

OUI

en fait c ce que je voulais dire ...... mais pas comme il le fallait

merci encore

Hors ligne

#6 10/01/2011 21:40:47

gilou974
Membre

Re : Fonctions imbriquées -- optimisation

Merci encore

juste pour dire, en suivant les conseils et en mettant tout dans une seule et grosse requete le tps d'exécution tombe, que dis-je : CHUTE à 14 secondes....... ;-)

Hors ligne

#7 10/01/2011 21:58:54

gleu
Administrateur

Re : Fonctions imbriquées -- optimisation

Ouch, impressionnant. Bien joué !


Guillaume.

Hors ligne

#8 12/01/2011 18:51:43

SQLpro
Membre

Re : Fonctions imbriquées -- optimisation

gilou974 a écrit :

Merci encore

juste pour dire, en suivant les conseils et en mettant tout dans une seule et grosse requete le tps d'exécution tombe, que dis-je : CHUTE à 14 secondes....... ;-)

Normal; un traitement ensembliste (requêtes SQL)  est optimisable. C'est d'ailleurs le rôle de l'optimiseur !  Et tout bon SGBDR comme PostGreSQL en est doté !!!
Un traitement itératif ne l'est pas car le code est statique !

De plus en ajoutant les bons index, vous arriverez certainement à diminuer encore les temps de réponse !

A +


Frédéric Brouard, alias SQLpro,  ARCHITECTE DE DONNÉES,  Expert langage SQL
Le site sur les SGBD relationnel et langage SQL   : http://sqlpro.developpez.com/
Modélisation de données, conseil, expertise, audit, optimisation, tuning, formation
* * * * *  Enseignant CNAM PACA, ISEN Toulon,  CESI Aix en Provence  * * * * *

Hors ligne

Pied de page des forums