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

SQL Oracle Discussion :

[SQL Dynamique] Problème d'exécution d'une requete d'update dans une fonction


Sujet :

SQL Oracle

  1. #1
    Membre à l'essai
    Homme Profil pro
    Ingénieur d'études
    Inscrit en
    Juin 2004
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur d'études
    Secteur : Conseil

    Informations forums :
    Inscription : Juin 2004
    Messages : 21
    Points : 17
    Points
    17
    Par défaut [SQL Dynamique] Problème d'exécution d'une requete d'update dans une fonction
    Bonjour,
    Je suis sur Oracle 9i et je développe une application pour les cours et je suis bloqué sur un points non vu.
    On doit faire un logiciel PHP/Oracle, si j'ai bien compris, la seule façon de "parler" avec le PHP c'est les fonctions. Me voilà parti à en exécuter une
    Voilà mon problème :
    J'ai créé une fontion qui va renvoyer 1 ou 0, jusque là aucun problème.
    Mais dans ma fonction, je me suis rendu compte que je pouvais faire de la maintenant en modifiant la base profitant de mes tests.

    Bref je fais tous plein de tests pour savoir si je vais renvoyer 1 ou 0, je fais mes select avec INTO ça marche très bien. Je veux faire un update.
    J'ai vérifié, ma requete marche très bien en test en direct donc elle est bonne.
    J'ai vérifié dans une procédure pour profiter du dbms_output, elle est toujours bonne. Donc le problème ne vient pas de la requete. hors j'ai l'erreur :
    impossible d'effectuer une opération DML dans une interrogation

    Je me suis dit, OK on va procéder autrement par un execute immediate.
    J'ai envoyé la requete dans une variable et j'ai evoyé un execute immediate :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    ligne_commande:={mon update...je vous épargne la taille de la ligne};
    execute immediate ligne_commande;
    Hors j'obtiens l'erreur ORA-00911: Caractère non valide
    Pile sur la ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    execute immediate ligne_commande;
    Voilà, ayant repris le code d'une des question résolue ici, je comprend vraiment pas ce qui ne va pas. Que ce soit dans une solution comme dans l'autre.

    En vous remerciant d'avance.

  2. #2
    Expert éminent sénior
    Avatar de orafrance
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    15 967
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France

    Informations forums :
    Inscription : Janvier 2004
    Messages : 15 967
    Points : 19 073
    Points
    19 073
    Par défaut
    le problème étant que tu nous épargnes la commande exécutée, j'vois mal comment on pourrait t'aider... en plus, pour du débuggage, je pense que tu sauras te débrouiller seul en affichant le contenu de ligne_commande et en le copiant sans SQL*Plus

  3. #3
    Membre expert

    Profil pro
    Inscrit en
    Février 2006
    Messages
    3 437
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 3 437
    Points : 3 597
    Points
    3 597
    Par défaut
    Peut-être qu'il suffit juste de supprimer le ';' final: il est nécessaire dans SQL*Plus mais il ne faut pas l'utiliser avec EXECUTE IMMEDIATE.

  4. #4
    Membre à l'essai
    Homme Profil pro
    Ingénieur d'études
    Inscrit en
    Juin 2004
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur d'études
    Secteur : Conseil

    Informations forums :
    Inscription : Juin 2004
    Messages : 21
    Points : 17
    Points
    17
    Par défaut
    Ok vous l'aurez voulu :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    ligne_commande:='UPDATE journee SET hfin_matin_jour=to_timestamp(''' || to_char(heure_depointage,'DD/MM/YY HH24:MI:SS') || ''',''DD/MM/YY HH24:MI:SS'') WHERE to_char(date_jour,''DD/MM/YY'')=''' || to_char(temps,'DD/MM/YY') || ''' AND rf_id_pers=' || pers;
     
    execute immediate ligne_commande;
    Voilà la ligne commande qui est exécutée, sinon pour l'histoire du ';' c'est pas ça, j'ai tenté sans le ';' c'est la même chose.
    Et pour le contenu de la ligne, tout est correct.

  5. #5
    Membre éprouvé Avatar de Yorglaa
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    845
    Détails du profil
    Informations personnelles :
    Âge : 52
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2004
    Messages : 845
    Points : 931
    Points
    931
    Par défaut
    Salut à tous,
    pour autant que je sache, il n'est pas possible avec Oracle de faire du DML (insert - update - delete) dans une FONCTION. Laquelle ne peut être invoquée que depuis un Select ou une assignation de valeur à une variable, ou encore un test de valeur de variable.

    avec Oracle il faut faire une PROCEDURE qui retourne un paramètre OUT.
    Il est plus facile de voir les signes avant-coureurs après coup que l'inverse !

    Yorglaa

  6. #6
    Membre à l'essai
    Homme Profil pro
    Ingénieur d'études
    Inscrit en
    Juin 2004
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur d'études
    Secteur : Conseil

    Informations forums :
    Inscription : Juin 2004
    Messages : 21
    Points : 17
    Points
    17
    Par défaut
    OK, peut-on, appeler une procédure à partir d'une fonction?Si oui comment, ça serait une feinte pour mon problème...

  7. #7
    Membre éprouvé Avatar de Yorglaa
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    845
    Détails du profil
    Informations personnelles :
    Âge : 52
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2004
    Messages : 845
    Points : 931
    Points
    931
    Par défaut
    oui et non...
    enfin oui tu peux appeler une procédure dans le code d'une fonction, mais non Oracle vas pas se laisser feinter comme ça... si l'appel initial est une fonction, le DML est interdit, même en cascade à travers une procédure imbriquée.

    voyons le problème en amont : pourquoi as-tu impérativement besoin d'une fonction ?
    Il est plus facile de voir les signes avant-coureurs après coup que l'inverse !

    Yorglaa

  8. #8
    Membre à l'essai
    Homme Profil pro
    Ingénieur d'études
    Inscrit en
    Juin 2004
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur d'études
    Secteur : Conseil

    Informations forums :
    Inscription : Juin 2004
    Messages : 21
    Points : 17
    Points
    17
    Par défaut
    En fait pour tout expliquer on fait un logiciel de pointage PHP/Oracle.
    En gros je dois donner au php l'information si la personne peut pointer ou pas.

    Dans la fonction je teste si le tuple qui contient le jour et la personne concernée existe dans la base, ça marche bien ça. Les select il n'y a pas de souci.
    Par contre si je trouve pas le tuple, je souhaite le créer par exemple d'où un insert (d'ailleur même "bug").

    Si la personne a déjà pointé le matin mais n'a pas dépointé à midi et qu'il est plus d'une certaine heure, je souhaite le dépointer en auto par le fameux update.

    pour simplifier voilà le shémas de la table sur laquelle je travaille :
    Table journee
    -----------------------------------------------------------------


    date_jour date
    hdeb_matin_jour timestamp
    hfin_matin_jour timestamp
    hdeb_am_jour timestamp
    hfin_am_jour timestamp
    #rf_id_pers integer

    -----------------------------------------------------------------

    En gros s'il n'y a pas de donnée c'est qu'il a pas pointé tout simplement.
    Pour chaque journée faite par la personne on crée un nouveau tuple.
    (je sais c'est pas top comme chémas bdd...)

    Voilà en gros, si le tuple existe pas j'aimerais pouvoir le créer.
    Et donc qu'ensuite la personne peut se loguer donc renvoyer
    l'info au php => 1 (0 en cas de pas de possibilité) d'où le fait
    que j'ai besoin d'une fonction.

  9. #9
    Membre éprouvé Avatar de Yorglaa
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    845
    Détails du profil
    Informations personnelles :
    Âge : 52
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2004
    Messages : 845
    Points : 931
    Points
    931
    Par défaut
    juste come ça, à vue de nez et à cette heure-ci, il me semble possible de tout faire passer par une procédure plutôt que par une fonction dans un Select...

    en gros, je verrais un principe du genre :
    - depuis php tu fais un appel de procédure Oracle, en lui fournissant les paramètres (IN) qui vont bien... personne_id et current_timestamp par exemple et un paramètre de sortie (OUT) qui te donne le résultat final.

    1. dans la procédure Oracle tu traite tous tes cas... par exemple : tu commences par ton test d'existence personne - jour de timbrage si oui tu continue le traitement si non (en gèrant l'exception no_data_found) tu fais l'insert voulu...
    2. ensuite tu testes l'éventuelle entrée/non-sortie en fonction de la "certaine heure" que tu cites (mise dans une autre variable dans ta proc Oracle) en la comparant avec ton paramètre current_timestamp. là aussi tu peux facilement faire du conditionnel avec des IF ou des Select en gèrant l'exception no_data_found qui envoient les updates qui vont bien.
    3. au final, en fonction de tes règles métier, tu renseigne le paramètre OUT avec 0 ou 1 selon que tu considère l'opération réussie ou pas...


    voilà, c'est très schématiquement suggéré, mais je pense qu'il y a moyen de faire qqchose comme ça...
    Il est plus facile de voir les signes avant-coureurs après coup que l'inverse !

    Yorglaa

  10. #10
    Membre à l'essai
    Homme Profil pro
    Ingénieur d'études
    Inscrit en
    Juin 2004
    Messages
    21
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur d'études
    Secteur : Conseil

    Informations forums :
    Inscription : Juin 2004
    Messages : 21
    Points : 17
    Points
    17
    Par défaut
    Ok, je comprend, je sais que c'est pas le lieu pour faire des déclarations d'amour, mais je t'aime ça se trouve sans le savoir tu viens de nous sauver (moi et mon binome) pour ce projet.
    Je vais passer par une procédure donc.
    Si j'ai bien compris le principe, le IN c'est le paramètre passé en entrée mettons :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    CREATE OR REPLACE PROCEDURE toto
    (
           pers IN INTEGER
    )
    par contre pour le OUT j'ai pas encore bien compris...
    je suis en train de regarder le tuto de développez.com sur le PL/SQL c'est pas encore super super bien expliqué (si je peux me permettre)...

    Pour le moment à priori si j'ai bien compris je pourrais déclarer comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    CREATE OR REPLACE PROCEDURE toto
    (
           pers IN INTEGER
          ,pointe IN OUT NUMBER
    ) IS
    BEGIN 
    {...} bla bla bla je fais tous mes tests et je renseigne pointe avec 1 ou 0
    END;
    Je me trompe?

    Merci encore , vraiment merci

  11. #11
    Membre éprouvé Avatar de Yorglaa
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    845
    Détails du profil
    Informations personnelles :
    Âge : 52
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2004
    Messages : 845
    Points : 931
    Points
    931
    Par défaut
    oui, c'est plus ou moins ça...
    voilà un petit début, plein de trous à compléter ;-) ...

    mais ça pourrait bien te donner des idées..
    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
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    Create or Replace Procedure Pointage (
                                            nPers_id    IN  Number
                                            , tCurrent  IN  TimeStamp
                                            , pResult   OUT pls_Integer
                                         )
    Is
        nTest                   Number ;
        tLimitTime              TimeStamp := to_timestamp( bla bla bla... la limite à considérer pour après-midi) ;
     
    Begin
        -- on commence par le test d'existence, encapsulé dans un bloc Begin / End pour pouvoir avoir un bloc "Exception" juste pour cette partie
        Begin
            Select id_pers
            Into    nTest
            From    journee
            Where   (
                      hdeb_matin_jour = aujourdhui -- (je te laisse coder une peu quand même)
                        Or
                      hdeb_am_jour = aujourdhui -- on teste aussi l après-midi, si qqun avait congé le matin)
                    )
            And     id_pers = nPers_id ; -- c'est la paramètre IN no 1
     
            Exception
            When No_data_found  -- la ligne n'existe pas
            Then
                    Insert into journee
                    Values ( bla, bla, bla) ;
     
        End ;
     
        -- ensuite on teste les heures
        Begin
     
            If comparaison entre tCurrent (le paramète IN no2) et tLimitTime
            Then
                le traitement approprié ... dépend du test que tu fais en dessus ;-) ;
            Elsif un éventuel autre test...
            Then
                un autre insert, update ou autre...
            End  If ;
     
        End ;
     
        Commit ;
        pResult := 1 ; -- succès !!
     
        Exception
        When Others -- toute exception non gérée plus haut
        Then
            Rollback ;
            pResult := 0 ; -- Echec !
     
     
    End ;
    ps j'ai fais a en vitesse sans trop checker la syntaxe et tout ça... alors indulgence !
    Il est plus facile de voir les signes avant-coureurs après coup que l'inverse !

    Yorglaa

  12. #12
    Membre éprouvé Avatar de Yorglaa
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    845
    Détails du profil
    Informations personnelles :
    Âge : 52
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2004
    Messages : 845
    Points : 931
    Points
    931
    Par défaut
    j'ajoute juste que comme alternative, tu peux aussi déterminer le current_timestamp dans une variable en début de procédure et pas comme un paramètre IN... ça évite de devoir le donner en paramètre, donc d'éviter les tricheries potentielles (l'heures courante sera celle du serveur DB, donc hors de portée du User frauduleux) !

    mais ça dépend du mode de saisie :
    - le user saisit son pointage via son PC (dont il peut modifier l'horloge) ?
    - c'est une machine automatique ?
    - autre ?

    le risque étant un serveur DB situé sur un autre fuseau horaire (pour les multi nationales) où il faudra aussi gèrer le paramètre TimeZone dans la procédure...
    Il est plus facile de voir les signes avant-coureurs après coup que l'inverse !

    Yorglaa

  13. #13
    Membre expert

    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Janvier 2004
    Messages
    2 862
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2004
    Messages : 2 862
    Points : 3 609
    Points
    3 609
    Par défaut
    Citation Envoyé par Yorglaa
    pour autant que je sache, il n'est pas possible avec Oracle de faire du DML (insert - update - delete) dans une FONCTION. Laquelle ne peut être invoquée que depuis un Select ou une assignation de valeur à une variable, ou encore un test de valeur de variable.
    Bonjour Yorglaa,
    Je me permets de modérer tes propos , il est tout à fait possible de faire du DML sous Oracle ... en PL/SQL. Par contre il n'est pas possible d'utiliser cette fonction dans un ordre DML :
    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
    31
    SQL> create table t (a number);
    Table created.
     
    SQL> create or replace function f return number is
      2  begin
      3    insert into t values (1);
      4    return 1;
      5  end;
      6  /
    Function created.
     
    SQL> declare
      2    a number;
      3  begin
      4    a := f;
      5  end;
      6  /
    PL/SQL procedure successfully completed.
     
    SQL> select * from t;
            A
    ---------
            1
     
    SQL> select f from dual;
    select f from dual
           *
    ERROR at line 1:
    ORA-14551: cannot perform a DML operation inside a query
    ORA-06512: at "COMPTA.F", line 3
    ORA-06512: at line 1
    Un problème sans solution est un problème mal posé

    Merci de poser vos questions sur le forum, je ne réponds pas aux questions posées par MP.

  14. #14
    Membre éprouvé Avatar de Yorglaa
    Profil pro
    Inscrit en
    Janvier 2004
    Messages
    845
    Détails du profil
    Informations personnelles :
    Âge : 52
    Localisation : Suisse

    Informations forums :
    Inscription : Janvier 2004
    Messages : 845
    Points : 931
    Points
    931
    Par défaut
    @plaineR

    merci pour cette précision !
    mais il est vrai que dans le cas qui nous occupe, notre ami essayait d'utiliser cette fonction dans une Select...

    Ceci dit, je préfère largement ne pas faire de DML dans les fonctions et utiliser les procédures pour ça...
    ça me permet aussi de ne pas avoir à me poser de question lorsque je veux utiliser une fonction dans un Select... je sais qu'elle fonctionnera, et qu'il n'y aura jamais de DML dans une fonction que j'aurais développé !

    mais chaque développeur aura sa popre politique en la matière !
    Il est plus facile de voir les signes avant-coureurs après coup que l'inverse !

    Yorglaa

  15. #15
    Membre expert

    Homme Profil pro
    Chef de projet en SSII
    Inscrit en
    Janvier 2004
    Messages
    2 862
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Chef de projet en SSII
    Secteur : Conseil

    Informations forums :
    Inscription : Janvier 2004
    Messages : 2 862
    Points : 3 609
    Points
    3 609
    Par défaut
    Citation Envoyé par Yorglaa
    Ceci dit, je préfère largement ne pas faire de DML dans les fonctions et utiliser les procédures pour ça...
    ça me permet aussi de ne pas avoir à me poser de question lorsque je veux utiliser une fonction dans un Select... je sais qu'elle fonctionnera, et qu'il n'y aura jamais de DML dans une fonction que j'aurais développé !
    Je suis entièrement d'accord avec toi
    Un problème sans solution est un problème mal posé

    Merci de poser vos questions sur le forum, je ne réponds pas aux questions posées par MP.

Discussions similaires

  1. Réponses: 3
    Dernier message: 23/01/2012, 14h50
  2. Réponses: 1
    Dernier message: 26/12/2010, 21h20
  3. Réponses: 9
    Dernier message: 26/11/2008, 10h18
  4. Réponses: 2
    Dernier message: 18/04/2008, 11h41
  5. insertion dans une table puis update dans une autre table
    Par uptoditime dans le forum VBA Access
    Réponses: 5
    Dernier message: 10/10/2007, 18h08

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