IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Requêtes PostgreSQL Discussion :

Fonction, cursor, dynamique sql & perfs


Sujet :

Requêtes PostgreSQL

  1. #1
    Membre expérimenté
    Profil pro
    Inscrit en
    Février 2004
    Messages
    1 824
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 1 824
    Points : 1 544
    Points
    1 544
    Par défaut Fonction, cursor, dynamique sql & perfs
    Bonjour,

    J'ai une table qui référence des codes iso et une autre table qui possède des colonnes selon le code iso.

    Par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    CREATE TABLE t_iso(
       id BIGSERIAL,
       code CHAR(2)
    );
    INSERT INTO t_iso (code) VALUES ('fr');
    INSERT INTO t_iso (code) VALUES ('en');
    INSERT INTO t_iso (code) VALUES ('be');
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    CREATE TABLE t_ref (
       id BIGSERIAL,
       name CHARACTER VARYING(255),
       fr INTERGER,
       en INTERGER,
       be INTERGER
    );
    INSERT INTO t_ref (name, fr, en, be) VALUES ('test1', 1, 2, null);
    INSERT INTO t_ref (name, fr, en, be) VALUES ('test2', null, 3, 4);
    INSERT INTO t_ref (name, fr, en, be) VALUES ('test1', 5, 6, 7);
    Je n'ai pas le choix sur la structure de ces deux tables, ça m'est imposé.

    Je dois faire une table reliant le t_ref.name avec les t_iso lorsque la valeur n'est pas null :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    CREATE TABLE t_ref_2(
       name CHARACTER VARYING(255),
       id_iso BIGINT REFERENCES t_iso(id),
       val INTERGER
    );
    Peut-être naivement, j'ai fait une procédure stockée avec un curseur sur la table des iso et du dynamic sql pour gérer le nom des colonnes, comme ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    CREATE FUNCTION fct_fill() RETURNS VOID AS $PROC$
    	DECLARE
    		r t_iso%rowtype;
    	BEGIN
    		FOR r IN SELECT * FROM t_iso LOOP
    			EXECUTE '
    				INSERT INTO t_ref_2(name, id_iso, val)
    					SELECT
    						name
    						r.id,
    						' || r.code || '
    					FROM
    						t_ref ref
    					WHERE
    						ref.' || r.code || ' IS NOT NULL
    			';
    		END LOOP;
    	END;
    $PROC$ language 'plpgsql';
    SELECT fct_fill();
    (C'est approximatif, je n'ai pas le code sous les yeux, mais voilà la démarche)

    Le soucis c'est que c'est long à s'exécuter, le disque ne gratte pas et le process est à 100%

    Dans mon contexte, il y a 649 codes iso différents (sur 2 et 3 caractère) et plus de 400 milles lignes dans t_ref.

    En analysant un peu, j'ai fait un "RAISE NOTICE" dans la boucle du curseur et au début ça enchaîne bien (à peu près une 20ène "d'EXECUTE" par seconde) puis au fur et à mesure ça ralenti jusqu'à demander plus de 10 secondes sur les 100 dernières boucles.

    En soit c'est pas illogique quant au fait que le serveur doit conserver les résultats au fur et à mesure que les requêtes s'exécutent au sein de cette transaction implicite avant de valider l'ensemble en cas de succès ou d'en faire un rollback en cas d'échec et les données s'accumulent d'où le ralentissement.

    Mais il y a sûrement moyen d'optimiser ça non ?

    - Soit sur une espèce d'autocommit activée spécialement pour l'occaz ?
    - Soit via une seule requête sans passer par une fonction / curseur / EXECUTE ?

    Merci pour vos réponses,

    A bientôt
    "Heureusement qu'il y avait mon nez, sinon je l'aurais pris en pleine gueule" Walter Spanghero

  2. #2
    Modérateur
    Avatar de al1_24
    Homme Profil pro
    Retraité
    Inscrit en
    Mai 2002
    Messages
    9 080
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 63
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 9 080
    Points : 30 789
    Points
    30 789
    Par défaut
    Pourquoi se fatiguer à faire une procédure et une boucle quand une simple requête peut faire tout le travail ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    INSERT INTO t_ref_2
        (   name
        ,   id_iso
        ,   val
        )
    SELECT  src.name
        ,   iso.id  AS id_iso
        ,   src.val
    FROM    (   SELECT  ref.name
                    ,   'fr'    AS  code
                    ,   ref.fr  AS  val
                FROM    t_ref   AS ref
                WHERE   ref.fr  IS NOT NULL
            UNION ALL
                SELECT  ref.name
                    ,   'en'    AS  code
                    ,   ref.en  AS  val
                FROM    t_ref   AS ref
                WHERE   ref.en  IS NOT NULL
            UNION ALL
                SELECT  ref.name
                    ,   'be'    AS  code
                    ,   ref.be  AS  val
                FROM    t_ref   AS ref
                WHERE   ref.be  IS NOT NULL
            )   src
        INNER JOIN
            t_iso   iso
            ON  src.code    = iso.code
    ;
    Et encore, cela pourrait être encore plus facile si ton SGBD prend en charge l'instruction UNPIVOT.
    Mais comme tu ne l'as pas précisé...
    Modérateur Langage SQL
    Règles du forum Langage SQL à lire par tous, N'hésitez pas à consulter les cours SQL
    N'oubliez pas le bouton et pensez aux balises
    [code]
    Si une réponse vous a aidé à résoudre votre problème, n'oubliez pas de voter pour elle en cliquant sur
    Aide-toi et le forum t'aidera : Un problème exposé sans mentionner les tentatives de résolution infructueuses peut laisser supposer que le posteur attend qu'on fasse son travail à sa place... et ne donne pas envie d'y répondre.

  3. #3
    Membre expérimenté
    Profil pro
    Inscrit en
    Février 2004
    Messages
    1 824
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 1 824
    Points : 1 544
    Points
    1 544
    Par défaut
    Oups, désolé, je suis en 9.4.

    Tu penses que je peux faire des jointures sur 649 tables (nombre de code iso) ?

    Je me renseigne sur UNPIVOT, je connais pas

    Merci
    "Heureusement qu'il y avait mon nez, sinon je l'aurais pris en pleine gueule" Walter Spanghero

  4. #4
    Membre émérite
    Profil pro
    Inscrit en
    Octobre 2008
    Messages
    1 874
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 1 874
    Points : 2 890
    Points
    2 890
    Par défaut
    Ca produit combien de lignes dans t_ref2 ?

    Au max ça doit faire 649 x " 400 milles lignes dans t_ref", soit dans les 260 millions de lignes.
    Il faudrait connaitre la proportion de NULL dans les colonnes iso pour avoir une estimation réaliste.

    Quoiqu'il en soit, une optimisation possible est d'attendre la fin du remplissage de la table avant de créer la contrainte 'intégrité référentielle:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    id_iso BIGINT REFERENCES t_iso(id),
    Sinon elle est testée à chaque insert pour rien.

  5. #5
    Membre expérimenté
    Profil pro
    Inscrit en
    Février 2004
    Messages
    1 824
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2004
    Messages : 1 824
    Points : 1 544
    Points
    1 544
    Par défaut
    Citation Envoyé par estofilo Voir le message
    Ca produit combien de lignes dans t_ref2 ?

    Au max ça doit faire 649 x " 400 milles lignes dans t_ref", soit dans les 260 millions de lignes.
    Il faudrait connaitre la proportion de NULL dans les colonnes iso pour avoir une estimation réaliste.

    Quoiqu'il en soit, une optimisation possible est d'attendre la fin du remplissage de la table avant de créer la contrainte 'intégrité référentielle:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    id_iso BIGINT REFERENCES t_iso(id),
    Sinon elle est testée à chaque insert pour rien.
    Bonjour,

    Merci pour ta réponse.

    Je pourrai te dire ce chiffre ce soir, mais dans t_ref il y a 649 colonnes "iso" qui contiennent toutes au moins une colonne avec une valeur dedans, et 1 à 2% qui ont entre 2 et 20 valeurs.

    Je vais retirer les contraintes pour les rajouter à la fin du script.

    A bientôt
    "Heureusement qu'il y avait mon nez, sinon je l'aurais pris en pleine gueule" Walter Spanghero

Discussions similaires

  1. Réponses: 17
    Dernier message: 13/07/2006, 12h52
  2. les cursor sur sql serveur
    Par hoummass dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 03/10/2005, 17h10
  3. Réponses: 13
    Dernier message: 20/06/2005, 14h13
  4. Fonction "Format" sous SQL
    Par Fabby69 dans le forum MS SQL Server
    Réponses: 7
    Dernier message: 08/10/2004, 09h07
  5. fonction left avec sql server 6.5
    Par shake dans le forum Langage SQL
    Réponses: 2
    Dernier message: 29/06/2004, 08h48

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo