Oracle:如果行不存在,如何INSERT

在PL / SQL(oracle)中插入一行,如果它不存在,最简单的方法是什么?

我想要的东西是:

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

但它不工作。

注意:这个表有2个字段,比如名字年龄 。 但是只有名字是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' ) 

假设你在10g上,你也可以使用MERGE语句。 这允许您插入行,如果它不存在,并忽略该行,如果它存在。 当他们想要做一个“upsert”的时候,人们往往会想到MERGE(INSERT如果该行不存在,UPDATE如果该行存在),但UPDATE部分现在是可选的,所以它也可以在这里使用。

 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 

如果名字是一个PK,那么插入并捕获错误。 这样做而不是任何检查的原因是即使多个客户端同时插入,它也可以工作。 如果您检查并插入,那么您必须在此期间持有锁,或者预期错误。

这个代码会是这样的

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

使用@benoit的部分答案,我将使用这个:

 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; 

对不起,我没有使用任何完整的给定的答案,但我需要IF检查,因为我的代码比这个名称和年龄字段的示例表复杂得多。 我需要一个非常明确的代码。 非常感谢,我学到了很多! 我会接受@benoit的答案。

我发现这些例子有点棘手,需要确保目标表中存在一行(特别是当您有两列作为主键时),但主键可能根本就不存在,所以没有任何东西select。

这对我来说是有效的:

 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' ) 

关键是:

  • USING块内的SELECT语句必须总是返回行。 如果没有从此查询返回的行,则不会插入或更新行。 在这里,我从DUALselect,总是只有一行。
  • ON条件是设置匹配行的条件。 如果ON没有匹配,则运行INSERT语句。
  • 如果您还想更多地控制更新,也可以添加WHEN MATCHED THEN UPDATE子句。

除了迄今给出的完美和有效的答案之外,还有您可能想要使用的ignore_row_on_dupkey_index提示:

 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; 

提示在大溪地描述。

你可以使用这个语法:

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

如果它打开一个popup询问为“input替代variables”,然后在上述查询之前使用此:

 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'); 

CTE和只有CTE 🙂

只是扔掉额外的东西。 对于所有的生活情况来说,这几乎是完整而冗长的forms。 你可以使用任何简洁的forms。

 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 )); 

我称之为“ 如果不存在 ”类固醇。 所以,这有助于我和我主要是这样做。