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

PL/SQL Oracle Discussion :

Fonctions Pipelined et tables mutantes


Sujet :

PL/SQL Oracle

  1. #1
    Membre régulier
    Homme Profil pro
    Expert Oracle
    Inscrit en
    Mai 2009
    Messages
    92
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Expert Oracle

    Informations forums :
    Inscription : Mai 2009
    Messages : 92
    Points : 70
    Points
    70
    Par défaut Fonctions Pipelined et tables mutantes
    Bonjour,

    Je fais des insertions en utilisant les fonctions Pipelined. J'insère à partir d'une table source T_SOURCE vers une table cible T_CIBLE, qui ont la structure suivante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    CREATE TABLE T_SOURCE (entite char(10), memo char(1000));
    CREATE TABLE T_CIBLE (entite char(10), seq number(4,0), memo char(1000));
    (PK de la table T_CIBLE est entite,seq)

    La commande pour insérer est la suivante:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    BEGIN
    	INSERT  /*+ PARALLEL(t, 10) */
    	INTO    T_CIBLE t
    	SELECT  *
    	FROM    TABLE(
    				MyPackage_1.PIPE_CIBLE ( SYSDATE,
    								     CURSOR(SELECT /*+ PARALLEL(p, 10) */ * FROM T_SOURCE p)));
    EXCEPTION
    WHEN OTHERS THEN
    	/*SORTIR EN ERREUR*/
    END;
    A l'intérieur de ma fonction PIPE_CIBLE, je dois calculer moi-même la séquence qui va être insérée dans T_CIBLE (pour éviter PK), pour ça je suis obligé de faire:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    SELECT  NVL(MAX(seq), 0) + 1
    INTO    t_cible_record.seq
    FROM    T_CIBLE
    WHERE   entite = t_cible_record.entite;
    Mais je rencontre le problème de table mutante, vu que j'insère dans T_CIBLE et en même temps j'essaie de faire un SELECT sur la même table !!

    Je cherche donc à savoir quel est le moyen le plus performant pour calculer ma séquence sans avoir de contrainte PK.

    Merci d'avance

  2. #2
    Expert éminent sénior Avatar de mnitu
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2007
    Messages
    5 611
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Octobre 2007
    Messages : 5 611
    Points : 11 252
    Points
    11 252
    Par défaut
    Calculez la séquence dans le select sur la table source en utilisant par exemple un outer join avec la table cible et la fonction analytique row_number()

  3. #3
    Modérateur
    Avatar de Waldar
    Homme Profil pro
    Customer Success Manager @Vertica
    Inscrit en
    Septembre 2008
    Messages
    8 452
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Customer Success Manager @Vertica
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2008
    Messages : 8 452
    Points : 17 820
    Points
    17 820

  4. #4
    Membre régulier
    Homme Profil pro
    Expert Oracle
    Inscrit en
    Mai 2009
    Messages
    92
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Expert Oracle

    Informations forums :
    Inscription : Mai 2009
    Messages : 92
    Points : 70
    Points
    70
    Par défaut
    La fonction ROW_NUMBER() ne pourra pas fonctionner dans ce cas, la table pouvant contenir des millions de lignes, alors que la séquence n'est qu'un NUMBER(4,0) !

    La solution temporaire que j'ai choisie est de créer une table intermédiaire (similaire à T_CIBLE) et faire comme suit:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    CREATE TABLE T_CIBLE_TMP (entite char(10), seq number(4,0), memo char(1000));
     
    INSERT  /*+ PARALLEL(t, 10) */
    INTO    T_CIBLE_TMP t
    SELECT  *
    FROM    TABLE(
    			MyPackage_1.PIPE_CIBLE_TMP ( SYSDATE,
                                               CURSOR(SELECT /*+ PARALLEL(p, 10) */ * FROM T_SOURCE p)));
     
     
    INSERT  /*+ PARALLEL(t, 10) */
    INTO    T_CIBLE t
    SELECT  *
    FROM    T_CIBLE_TMP;
    Dans la fonction PIPE_CIBLE_TMP je fait mon SELECT MAX FROM T_CIBLE pour récupérer la bonne séquence. Cette solution n'est sûrement pas la bonne, mais c'est ce que j'ai trouvé de mieux pour l'instant !!

  5. #5
    Modérateur
    Avatar de Waldar
    Homme Profil pro
    Customer Success Manager @Vertica
    Inscrit en
    Septembre 2008
    Messages
    8 452
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Customer Success Manager @Vertica
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2008
    Messages : 8 452
    Points : 17 820
    Points
    17 820
    Par défaut
    Citation Envoyé par labolabs Voir le message
    La fonction ROW_NUMBER() ne pourra pas fonctionner dans ce cas, la table pouvant contenir des millions de lignes, alors que la séquence n'est qu'un NUMBER(4,0) !
    Aucun rapport !
    Comment calculez-vous votre numéro de séquence ?

  6. #6
    Membre régulier
    Homme Profil pro
    Expert Oracle
    Inscrit en
    Mai 2009
    Messages
    92
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Expert Oracle

    Informations forums :
    Inscription : Mai 2009
    Messages : 92
    Points : 70
    Points
    70
    Par défaut
    Normalement on incrémente la séquence à chaque fois que la même entité est ajoutée. Si je veux insérer 2 mémos pour la même entité 'AAA', j'aurais dans ma table:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    ENTITE || SEQ || MEMO
    'AAA' || 1 || 'xxxxxxxxxxxxxxxxxxxx'
    'AAA' || 2 || 'xxxxxxxxxxxxxxxxxxxx'
    'BBB' || 1 || 'xxxxxxxxxxxxxxxxxxxx'
    'AAA' || 3 || 'xxxxxxxxxxxxxxxxxxxx'
    'BBB' || 2 || 'xxxxxxxxxxxxxxxxxxxx'
    Le max sera:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    'AAA' || 9999 || 'xxxxxxxxxxxxxxxxxxxx'
    Pouvez-vous me donner un example avec ROW_NUMBER() ?

  7. #7
    Modérateur
    Avatar de Waldar
    Homme Profil pro
    Customer Success Manager @Vertica
    Inscrit en
    Septembre 2008
    Messages
    8 452
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Customer Success Manager @Vertica
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2008
    Messages : 8 452
    Points : 17 820
    Points
    17 820
    Par défaut
    Par exemple :
    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
    select entite,
           row_number() over(partition by entite order by rownum asc) as seq,
           memo
      from
    (
    select 'AAA' as entite, 'xxxxxxxxxxxxxxxxxxxx' as memo from dual union all
    select 'AAA'          , 'xxxxxxxxxxxxxxxxxxxx'         from dual union all
    select 'BBB'          , 'xxxxxxxxxxxxxxxxxxxx'         from dual union all
    select 'AAA'          , 'xxxxxxxxxxxxxxxxxxxx'         from dual union all
    select 'BBB'          , 'xxxxxxxxxxxxxxxxxxxx'         from dual
    )
    order by rownum asc;
     
    ENTITE	SEQ	MEMO
    AAA	1	xxxxxxxxxxxxxxxxxxxx
    AAA	2	xxxxxxxxxxxxxxxxxxxx
    BBB	1	xxxxxxxxxxxxxxxxxxxx
    AAA	3	xxxxxxxxxxxxxxxxxxxx
    BBB	2	xxxxxxxxxxxxxxxxxxxx

  8. #8
    Expert éminent sénior Avatar de mnitu
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2007
    Messages
    5 611
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Octobre 2007
    Messages : 5 611
    Points : 11 252
    Points
    11 252
    Par défaut
    Si la table destination contient déjà la valeur entite avec une séquence quand vous commencez à charger les données, vous avez besoin d'ajouter un delta à la valeur retournée par la fonction row_number(). D'où le jointure externe avec la table cible pour obtenir cette valeur delta pour chaque entité.

  9. #9
    Membre régulier
    Homme Profil pro
    Expert Oracle
    Inscrit en
    Mai 2009
    Messages
    92
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Expert Oracle

    Informations forums :
    Inscription : Mai 2009
    Messages : 92
    Points : 70
    Points
    70
    Par défaut
    C'est cool la fonction ROW_NUMBER() marche très bien

    Merci les gars cette astuce !

  10. #10
    Membre régulier
    Homme Profil pro
    Expert Oracle
    Inscrit en
    Mai 2009
    Messages
    92
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Expert Oracle

    Informations forums :
    Inscription : Mai 2009
    Messages : 92
    Points : 70
    Points
    70
    Par défaut
    Voici le code final:

    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
    BEGIN
    	INSERT  /*+ PARALLEL(t, 10) */
    	INTO    T_CIBLE t
    	SELECT  tab.entite
    			ROW_NUMBER() OVER(PARTITION BY tab.entite ORDER BY ROWNUM) AS seq,
    			tab.memo
    	FROM    TABLE(
    				MyPackage_1.PIPE_CIBLE ( SYSDATE,
    										CURSOR(SELECT /*+ PARALLEL(p, 10) */ * FROM T_SOURCE p))) tab
    	LEFT OUTER JOIN T_CIBLE tc
    	ON	tc.entite = tab.entite;
    EXCEPTION
    WHEN OTHERS THEN
    	/*SORTIR EN ERREUR*/
    END;
    Bon ça insère les lignes sans erreur, mais côté performances j'ai encore des doutes !!

    Petit exemple:

    • Première exécution: 0 ligne dans T_CIBLE, 3 millions de lignes dans T_SOURCE, durée d'exécution: 13min50s
    • Deuxième exécution: 3 millions de lignes dans T_CIBLE, 3 millions de lignes dans T_SOURCE, durée d'exécution: 43min12s


    Si j'enlève la jointure externe, la première exécution ne fait que 5min10s !!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    BEGIN
    	INSERT  /*+ PARALLEL(t, 10) */
    	INTO    T_CIBLE t
    	SELECT  entite
    			ROW_NUMBER() OVER(PARTITION BY entite ORDER BY ROWNUM) AS seq,
    			memo
    	FROM    TABLE(
    				MyPackage_1.PIPE_CIBLE ( SYSDATE,
    										CURSOR(SELECT /*+ PARALLEL(p, 10) */ * FROM T_SOURCE p)));
    EXCEPTION
    WHEN OTHERS THEN
    	/*SORTIR EN ERREUR*/
    END;

  11. #11
    Expert éminent sénior Avatar de mnitu
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Octobre 2007
    Messages
    5 611
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Octobre 2007
    Messages : 5 611
    Points : 11 252
    Points
    11 252
    Par défaut
    Les fonctions Pipelined sint loin d’être l'idéal pour faire des jointures avec. Pensez à l'optimiseur, il a besoin d'aide dans ce cas.
    Pour que le traitement soit performant la jointure doit se faire à l'intérieur de la fonction pipelined. Le but étant de faire simplement un traitement de type
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Insert Into Cible
      Select From Table(f_pipeline(arg))

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Fonction et returns table
    Par Mercenary Developer dans le forum MS SQL Server
    Réponses: 8
    Dernier message: 19/10/2009, 17h27
  2. Table Mutante
    Par chiheb dans le forum Oracle
    Réponses: 5
    Dernier message: 24/04/2006, 16h22
  3. Réponses: 7
    Dernier message: 28/02/2006, 16h46
  4. update en fonction de 2 tables
    Par logisoftfr dans le forum Langage SQL
    Réponses: 2
    Dernier message: 13/01/2006, 19h33
  5. Réponses: 4
    Dernier message: 30/10/2005, 09h13

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