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 :

requete DELETE multi-tables


Sujet :

SQL Oracle

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    22
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 22
    Par défaut requete DELETE multi-tables
    Bonjour à tous,

    Je cherche à faire une requête de suppression multi-tables avec Oracle 10g.

    Je voudrais supprimer tous les éléments retournés par la requête suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    SELECT * FROM TAB_ARA, TAB_VIR
         WHERE TAB_ARA.ARA_NUM_COMPTE_CLI = TAB_VIR.VIR_NUM_COMPTE_EMET;
    j'ai donc naturellement lancé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    DELETE FROM TAB_ARA, TAB_VIR
         WHERE TAB_ARA.ARA_NUM_COMPTE_CLI = TAB_VIR.VIR_NUM_COMPTE_EMET);
    mais ca ne marche pas :
    DELETE FROM TAB_ARA, TAB_VIR
    ...............................*
    ERROR at line 1:
    ORA-00933: SQL command not properly ended
    du coup, j'ai essayé de faire une requête imbriquée :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    DELETE FROM 
        ( SELECT * FROM TAB_ARA, TAB_VIR
         WHERE TAB_ARA.ARA_NUM_COMPTE_CLI = TAB_VIR.VIR_NUM_COMPTE_EMET);
    et ca renvoie :
    ORA-01752: cannot delete from view without exactly one key-preserved table
    La page suivant explique comment faire :
    http://dev.mysql.com/doc/refman/5.0/fr/delete.html
    mais c'est pour mysql. J'ai rien trouvé qui marcherait avec Oracle.

    Pouvez-vous m'aider ?

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

    Informations forums :
    Inscription : Janvier 2004
    Messages : 15 967
    Par défaut
    c'est pas possible... le mieux c'est une foreign key avec DELETE CASCADE, quand tu supprimes le père ça supprime les fils

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    22
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 22
    Par défaut
    Merci pour la réponse mais il me semble qu'avec une foreign key, on doit avoir une colonne qui contient strictement les mêmes données dans les 2 tables ?

    moi je cherche à supprimer les lignes qui sont identiques, pour ne traiter que les cas problématiques.

    j'ai fait :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    CREATE TABLE test  AS ( SELECT * FROM TAB_ARA WHERE rownum<100); 
    CREATE TABLE test2  AS ( SELECT * FROM TAB_VIR WHERE rownum<100); 
    ALTER TABLE test ADD CONSTRAINT unicite FOREIGN KEY (ARA_NUM_COMPTE_CLI) REFERENCES test2(VIR_NUM_COMPTE_EMET);
    et il me renvoie "no matching unique or primary key for this column-list".
    ce qui me parait normal vu que je sais que les 2 colonnes ne sont pas identiques.

  4. #4
    Membre chevronné Avatar de NGasparotto
    Inscrit en
    Janvier 2007
    Messages
    421
    Détails du profil
    Informations forums :
    Inscription : Janvier 2007
    Messages : 421
    Par défaut
    Peitit exemple avec le DELETE CASCADE sur une FK, et ca marche très bien :
    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
    SQL> CREATE TABLE test_dup  AS ( SELECT object_name, object_id, 0 as oid FROM all_objects); 
     
    Table created.
     
    SQL> CREATE TABLE test2_dup  AS ( SELECT object_name, 0 as object_id, object_id as oid FROM test_dup); 
     
    Table created.
     
    SQL> ALTER TABLE test_dup ADD CONSTRAINT unicite_pk PRIMARY KEY (object_id);
     
    Table altered.
     
    SQL> ALTER TABLE test2_dup ADD CONSTRAINT unicite_fk FOREIGN KEY (oid) REFERENCES test_dup(object_id) on delete cascade;
     
    Table altered.
     
    SQL> 
    SQL> select object_id from test_dup where object_id <= 10;
     
     OBJECT_ID
    ----------
             3
             4
             5
             7
             9
     
    SQL> select oid from test2_dup where oid <= 10;
     
           OID
    ----------
             5
             9
             3
             7
             4
     
    SQL> 
    SQL> delete test_dup where object_id <= 10;
     
    5 rows deleted.
     
    SQL> 
    SQL> select object_id from test_dup where object_id <= 10;
     
    no rows selected
     
    SQL> select oid from test2_dup where oid <= 10;
     
    no rows selected
     
    SQL>
    Nicolas.

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    22
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 22
    Par défaut quelques précisions :
    Je cherche un moyen de supprimer des éléments qui se trouvent dans les 2 tables et ne garder dans chaque table que les éléments qui ne se trouvent pas dans l'autre.

    exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    drop table test;
    drop table test2;
    create table test (a number);
    create table test2 (b number);
    insert into test values (1);
    insert into test values (2);
    insert into test2 values (2);
    insert into test2 values (3);
     
    ALTER TABLE test ADD CONSTRAINT unicite_pk PRIMARY KEY (a);
    ALTER TABLE test2 ADD CONSTRAINT unicite_fk FOREIGN KEY (b) REFERENCES test(a) ON DELETE cascade;
    ORA-02298: cannot validate (BO.UNICITE_FK) - parent keys not found

    si j'insère les mêmes valeurs dans les 2 tables, la contrainte se crée.

    je croit que je vais regarder du coté là : http://oracle.developpez.com/faq/?pa...#errconstraint

    mais ca me parait quand meme bien compliqué ...

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    22
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 22
    Par défaut I think I found it
    Merci pour votre aide.

    un moyen pour faire ça est d'ajouter une foreign key sur les 2 tables et de tester les rejets de contraintes:

    1 - création de la table des exceptions :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    create table exceptions(row_id rowid, owner varchar2(30), table_name varchar2(30), constraint varchar2(30));
    2 - contraintes croisées :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    ALTER TABLE test ADD CONSTRAINT unicite_pk PRIMARY KEY (a);
    ALTER TABLE test2 ADD CONSTRAINT unicite2_pk PRIMARY KEY (b);
    ALTER TABLE test ADD CONSTRAINT unicite_fk FOREIGN KEY (a) REFERENCES test2(b) exceptions into exceptions;
    ALTER TABLE test2 ADD CONSTRAINT unicite2_fk FOREIGN KEY (b) REFERENCES test(a) exceptions into exceptions;
    3 - extraction depuis la table des exceptions :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    select * from test where rowid in ( select row_id from exceptions where table_name='TEST');
    select * from test2 where rowid in ( select row_id from exceptions where table_name='TEST2');
    Je teste tout de suite sur la vraie base.

  7. #7
    Membre averti
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    22
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 22
    Par défaut arg
    ORA-02437: cannot validate (BO.VIR_PK) - primary key violated

    les colonnes ARA_NUM_COMPTE_CLI et VIR_NUM_COMPTE_EMET ne peuvent pas être déclarées en tant que clés uniques.
    ca devient compliqué là.

  8. #8
    Membre chevronné Avatar de NGasparotto
    Inscrit en
    Janvier 2007
    Messages
    421
    Détails du profil
    Informations forums :
    Inscription : Janvier 2007
    Messages : 421
    Par défaut
    Citation Envoyé par Diwann
    supprimer des éléments qui se trouvent dans les 2 tables et ne garder dans chaque table que les éléments qui ne se trouvent pas dans l'autre.
    Je ne sais pas si tu pourras faire çà en une requête (est-ce vraiment nécessaire ?), mais avec un tout petit PL/SQL, çà peut être beaucoup plus simple (à voir tout de même pour les perf.) :
    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
    SQL> CREATE TABLE test (a number);
     
    Table created.
     
    SQL> CREATE TABLE test2 (b number);
     
    Table created.
     
    SQL> INSERT INTO test VALUES (1);
     
    1 row created.
     
    SQL> INSERT INTO test VALUES (2);
     
    1 row created.
     
    SQL> INSERT INTO test2 VALUES (2);
     
    1 row created.
     
    SQL> INSERT INTO test2 VALUES (3);
     
    1 row created.
     
    SQL> 
    SQL> begin
      2  for x in (select a from test where exists (select 1 from test2 where a=b)) loop
      3      delete test where a = x.a;
      4      delete test2 where b = x.a;
      5  end loop;
      6  end;
      7  /
     
    PL/SQL procedure successfully completed.
     
    SQL> 
    SQL> select * from test;
     
             A
    ----------
             1
     
    SQL> select * from test2;
     
             B
    ----------
             3
     
    SQL>
    Nicolas.

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    22
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 22
    Par défaut
    a ouais !
    trop fort.
    Merci beaucoup, je teste ca demain.

  10. #10
    Membre averti
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    22
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 22
    Par défaut
    Ca marche.
    Thanx a lot Nicolas.

    Je vais tester les perfs maintenant, mais ca devrait aller avec < 100 000 enregistrements.

  11. #11
    Membre averti
    Profil pro
    Inscrit en
    Mars 2002
    Messages
    22
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mars 2002
    Messages : 22
    Par défaut précisions sur les perfs
    Je reviens sur ce sujet pour signaler qu'après des tests réels, les perfs se sont révélées déplorables (plusieurs heures parfois pour 150 000 lignes); et pour expliquer comment on a pu réduire ça à quelques secondes.

    L'idée étant qu'il y a très peu d'enregistrements différents d'une table à l'autre, et qu'il y a des optimisations possible en diminuant le nombre de requete DELETE effectuées.

    on a donc créé une colonne supplémentaire pour indiquer les enregistrements qu'il faudra garder.
    On utilise alors le script de NGasparotto pour chercher les enregistrements présents seulement dans l'une des 2 tables en faisant un NOT EXISTS à la place du EXISTS, et à la fin on supprime en une requete tous les éléments qui ont cette colonne vide.

    code :
    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
    drop table test3;
    drop table test4;
    CREATE TABLE test3  AS ( SELECT * FROM TAB_ARA);
    CREATE TABLE test4  AS ( SELECT * FROM TAB_VIR);
    ALTER TABLE test3 ADD keep INT;
    ALTER TABLE test4 ADD keep INT;
     
    begin
        FOR x IN   (SELECT * FROM test4 WHERE NOT EXISTS 
                    (SELECT 1 FROM test3 WHERE a=b) )loop                                
            UPDATE test4 SET keep = 1 WHERE a = x.a;
        end loop;
     
        FOR x IN   (SELECT * FROM test3 WHERE NOT EXISTS 
                    (SELECT 1 FROM test4 WHERE a=b) ) loop
            UPDATE test3 SET keep = 1 WHERE b = x.b;
        end loop;   
     
        DELETE FROM test3 where keep IS NULL;
        DELETE FROM test4 where keep IS NULL;
        commit;
    end;
    /
    ALTER TABLE test3 DROP column keep;
    ALTER TABLE test4 DROP column keep;
    ce qui nous a permis de passer de 4 heures à 40 sec.

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

Discussions similaires

  1. delete multi-tables en hql
    Par gregounet dans le forum Hibernate
    Réponses: 3
    Dernier message: 05/05/2008, 19h27
  2. DELETE multi-tables avec jointures
    Par Xunil dans le forum Requêtes
    Réponses: 13
    Dernier message: 23/10/2006, 16h44
  3. [MySQL 3.23.58] delete multi-tables
    Par GLDavid dans le forum Requêtes
    Réponses: 8
    Dernier message: 07/08/2006, 11h45
  4. Problème DELETE multi-tables
    Par Gwipi dans le forum Requêtes
    Réponses: 5
    Dernier message: 22/05/2006, 08h48
  5. erreur de syntaxe DELETE multi-tables
    Par yayacameleon dans le forum Requêtes
    Réponses: 7
    Dernier message: 12/04/2006, 11h34

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