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.
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'
)
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
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;
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.
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:
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.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.WHEN MATCHED THEN UPDATE
si vous souhaitez davantage de contrôle sur les mises à jour.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 .
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');
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.