web-dev-qa-db-fra.com

Oracle: comment INSERER si une ligne n'existe pas

Quel est le moyen le plus simple d'insérer une ligne si elle n'existe pas, en PL/SQL (Oracle)?

Je veux quelque chose comme:

IF NOT EXISTS (SELECT * FROM table WHERE name = 'jonny') THEN
  INSERT INTO table VALUES ("jonny", null);
END IF;

Mais ça ne marche pas.

Remarque: cette table contient 2 champs, par exemple, name et age . Mais seulement name est PK.

43
Topera
INSERT INTO table
SELECT 'jonny', NULL
  FROM dual -- Not Oracle? No need for dual, drop that line
 WHERE NOT EXISTS (SELECT NULL -- canonical way, but you can select
                               -- anything as EXISTS only checks existence
                     FROM table
                    WHERE name = 'jonny'
                  )
63
Benoit

En supposant que vous soyez sur 10g, vous pouvez également utiliser l'instruction MERGE. Cela vous permet d'insérer la ligne si elle n'existe pas et d'ignorer la ligne si elle existe. Les gens ont tendance à penser à MERGE quand ils veulent faire un "upsert" (INSERT si la ligne n'existe pas et UPDATE si la ligne existe), mais la partie UPDATE est maintenant optionnelle et peut donc également être utilisée ici.

SQL> create table foo (
  2    name varchar2(10) primary key,
  3    age  number
  4  );

Table created.

SQL> ed
Wrote file afiedt.buf

  1  merge into foo a
  2    using (select 'johnny' name, null age from dual) b
  3       on (a.name = b.name)
  4   when not matched then
  5    insert( name, age)
  6*    values( b.name, b.age)
SQL> /

1 row merged.

SQL> /

0 rows merged.

SQL> select * from foo;

NAME              AGE
---------- ----------
johnny
31
Justin Cave

Si name est une PK, il suffit d'insérer et de détecter l'erreur. La raison pour laquelle cela est fait plutôt que toute vérification est que cela fonctionnera même avec plusieurs clients insérant en même temps. Si vous cochez puis insérez, vous devez garder un verrou pendant cette période ou vous attendrez quand même à l'erreur.

Le code pour ce serait quelque chose comme

BEGIN
  INSERT INTO table( name, age )
    VALUES( 'johnny', null );
EXCEPTION
  WHEN dup_val_on_index
  THEN
    NULL; -- Intentionally ignore duplicates
END;
14
Lou Franco

En utilisant des parties de @benoit answer, je vais utiliser ceci:

DECLARE
    varTmp NUMBER:=0;
BEGIN
    -- checks
    SELECT nvl((SELECT 1 FROM table WHERE name = 'john'), 0) INTO varTmp FROM dual;

    -- insert
    IF (varTmp = 1) THEN
        INSERT INTO table (john, null)
    END IF;

END;

Désolé, je n'utilise pas de réponse complète, mais j'ai besoin de la vérification IF car mon code est beaucoup plus complexe que cet exemple de table avec les champs name et age. J'ai besoin d'un code très clair. Eh bien merci, j'ai beaucoup appris! Je vais accepter la réponse @benoit.

9
Topera

J'ai trouvé les exemples un peu difficiles à suivre lorsque vous voulez vous assurer qu'une ligne existe dans la table de destination (surtout lorsque vous avez deux colonnes comme clé primaire), mais que la clé primaire n'existe peut-être pas du tout et qu'il n'y a rien pour sélectionner.

C'est ce qui a fonctionné pour moi:

MERGE INTO table1 D
    USING (
        -- These are the row(s) you want to insert.
        SELECT 
        'val1' AS FIELD_A,
        'val2' AS FIELD_B
        FROM DUAL

    ) S ON (
        -- This is the criteria to find the above row(s) in the
        -- destination table.  S refers to the rows in the SELECT
        -- statement above, D refers to the destination table.
        D.FIELD_A = S.FIELD_A
        AND D.FIELD_B = S.FIELD_B
    )

    -- This is the INSERT statement to run for each row that
    -- doesn't exist in the destination table.
    WHEN NOT MATCHED THEN INSERT (
        FIELD_A,
        FIELD_B,
        FIELD_C
    ) VALUES (
        S.FIELD_A,
        S.FIELD_B,
        'val3'
    )

Les points clés sont:

  • L'instruction SELECT à l'intérieur du bloc USING doit toujours renvoyer des lignes. S'il n'y a aucune ligne renvoyée par cette requête, aucune ligne ne sera insérée ni mise à jour. Ici, je choisis DUAL donc il y aura toujours exactement une ligne.
  • La condition ON est ce qui définit les critères de correspondance des lignes. Si ON ne correspond pas, l'instruction INSERT est exécutée.
  • Vous pouvez également ajouter une clause WHEN MATCHED THEN UPDATE si vous souhaitez davantage de contrôle sur les mises à jour.
7
Malvineous

En plus des réponses parfaites et valides données jusqu'à présent, il existe également l'indicateur ignore_row_on_dupkey_index que vous pouvez utiliser:

create table tq84_a (
  name varchar2 (20) primary key,
  age  number
);

insert /*+ ignore_row_on_dupkey_index(tq84_a(name)) */ into tq84_a values ('Johnny',   77);
insert /*+ ignore_row_on_dupkey_index(tq84_a(name)) */ into tq84_a values ('Pete'  ,   28);
insert /*+ ignore_row_on_dupkey_index(tq84_a(name)) */ into tq84_a values ('Sue'   ,   35);
insert /*+ ignore_row_on_dupkey_index(tq84_a(name)) */ into tq84_a values ('Johnny', null);

select * from tq84_a;

La suggestion est décrite sur Tahiti .

3
René Nyffenegger

vous pouvez utiliser cette syntaxe:

INSERT INTO table_name ( name, age )
select  'jonny', 18 from dual
where not exists(select 1 from table_name where name = 'jonny');

si son ouvre un pop pour demander comme "entrer une variable de substitution", utilisez ceci avant les requêtes ci-dessus: 

set define off;
INSERT INTO table_name ( name, age )
select  'jonny', 18 from dual
where not exists(select 1 from table_name where name = 'jonny');
1
user2667653

CTEet seulementCTE:-)

viens de jeter des trucs supplémentaires.Voici une forme presque complète et détaillée pour tous les cas de la vie. Et vous pouvez utiliser n'importe quel formulaire concis.

INSERT INTO reports r
  (r.id, r.name, r.key, r.param)

-

  -- Invoke this script from "WITH" to the end (";")
  -- to debug and see prepared values.
  WITH

  -- Some new data to add.
  newData AS(
          SELECT 'Name 1' name, 'key_new_1' key FROM DUAL
    UNION SELECT 'Name 2' NAME, 'key_new_2' key FROM DUAL
    UNION SELECT 'Name 3' NAME, 'key_new_3' key FROM DUAL
    ),
  -- Any single row for copying with each new row from "newData",
  -- if you will of course.
  copyData AS(
      SELECT r.*
      FROM reports r
      WHERE r.key = 'key_existing'
        -- ! Prevent more than one row to return.
        AND FALSE -- do something here for than!
    ),
  -- Last used ID from the "reports" table (it depends on your case).
  -- (not going to work with concurrent transactions)
  maxId AS (SELECT MAX(id) AS id FROM reports),

-

  -- Some construction of all data for insertion.
  SELECT maxId.id + ROWNUM, newData.name, newData.key, copyData.param
  FROM copyData
    -- matrix multiplication :)
    -- (or a recursion if you're imperative coder)
    CROSS JOIN newData
    CROSS JOIN maxId

-

  -- Let's prevent re-insertion.
  WHERE NOT EXISTS (
      SELECT 1 FROM reports rs
      WHERE rs.name IN(
        SELECT name FROM newData
      ));

Je l'appelle " SI PAS EXISTE " sur les stéroïdes. Donc, cela m'aide et je le fais surtout.

0
it3xl