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
语句必须总是返回行。 如果没有从此查询返回的行,则不会插入或更新行。 在这里,我从DUAL
select,总是只有一行。 -
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 ));
我称之为“ 如果不存在 ”类固醇。 所以,这有助于我和我主要是这样做。