在PL/SQL(oracle)中插入行如果不存在,最简单的方法是什么?Oracle:如果行不存在,如何插入
我想是这样的:
IF NOT EXISTS (SELECT * FROM table WHERE name = 'jonny') THEN
INSERT INTO table VALUES ("jonny", null);
END IF;
但它不工作。
注:此表中有2场,也就是说,名和年龄。但只有名称是PK。
在PL/SQL(oracle)中插入行如果不存在,最简单的方法是什么?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'
)
这段代码中的“dual”是什么?我肯定错过了什么。 – 2010-10-01 17:38:26
@Jeff Walker:[看到这个问题](http://stackoverflow.com/questions/3732422/select-from-nothing/3732466#3732466) – 2010-10-01 17:40:15
dual是Oracle中的一个虚拟表,有一列和一行。这是不好的(在SQLite中,你可以直接选择,而在Oracle中,你必须从无处选择时使用双重)。 – Benoit 2010-10-01 17:56:28
如果name是一个PK,那么只需插入并捕获错误。这样做而不是任何检查的原因是,即使多个客户端同时插入,它也可以工作。如果您检查并插入,则必须在此期间持有锁定,否则将会出现错误。
该代码,这会是这样的
BEGIN
INSERT INTO table(name, age)
VALUES('johnny', null);
EXCEPTION
WHEN dup_val_on_index
THEN
NULL; -- Intentionally ignore duplicates
END;
代码:'开始插入表值('jonny',null);例外时sqlcode!= -1然后升起;结束; /' sqlcode = -1当ORA-00001 – Benoit 2010-10-01 17:18:54
尝试插入和捕获异常是否合理取决于您期望INSERT成功的频率。如果99%的时间你插入一个非重复的值,并且它只会错误1%的时间,捕捉和忽略异常是一个不错的选择。如果行已经存在的时间为99%,从性能角度来看,捕获异常可能会产生问题。 – 2010-10-01 17:42:40
另外,合并方法可以在插入中使用多行...选择哪一个不这样做。 (我知道OP以单行为例,但为此(以及Justin Cave选址的性能问题),我认为合并是一个更好的解决方案。 – Adam 2014-04-22 12:24:43
假设你是在10g中,您还可以使用MERGE语句。这使您可以在行不存在的情况下插入行,并在行存在时忽略该行。当他们想要做一个“upsert”时,人们倾向于想到MERGE(INSERT如果该行不存在,则更新该行是否存在),但UPDATE部分现在是可选的,因此它也可以在这里使用。 @benoit答案
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
使用的部分,我会用这样的:
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的答案。
你可以使用这个语法:
INSERT INTO table_name (name, age)
select 'jonny', 18 from dual
where not exists(select 1 from table_name where name = 'jonny');
如果开放的要求为“输入替代变量”的流行则上述查询之前使用:
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');
这与三年前公布的已接受答案有什么不同? – Mat 2013-08-09 11:22:25
除到目前为止给出的完美和有效的答案,还有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;
提示在Tahiti上描述。
我发现这些例子有点棘手,需要确保目标表中存在一行(特别是当您有两列作为主键时),但主键可能不存在所有这一切都没有选择。
这是对我工作:
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
中选择,所以总是只有一行。ON
条件是设置匹配行的条件。如果ON
没有匹配,则运行INSERT语句。WHEN MATCHED THEN UPDATE
子句。CTE只有CTE :-)
只是抛出多余的东西。对于所有生活情况,这里几乎都是完整而冗长的形式。你可以使用任何简洁的形式。
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
));
我称之为 “IF NOT EXISTS” 类固醇。所以,这有助于我和我主要这样做。
您是否期望INSERT语句通常是必需的(即该行通常不会存在)?或者该行通常会存在? – 2010-10-01 17:39:24
@justin:行一般不会存在。 – Topera 2010-10-01 17:50:51
很酷。那么这里的三个选项中的任何一个都应该适合你。 – 2010-10-01 18:01:11