在PL / SQL(oracle)中,如果行不存在,最简单的方法是什么?
我想要类似的东西:
IF NOT EXISTS (SELECT * FROM table WHERE name = 'jonny') THEN
INSERT INTO table VALUES ("jonny", null);
END IF;
但它不起作用。
注意:此表格包含2个字段,例如名称和年龄。但只有名称才是PK。
答案 0 :(得分:66)
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'
)
答案 1 :(得分:32)
假设您使用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
答案 2 :(得分:14)
如果name是PK,那么只需插入并捕获错误。这样做而不是任何检查的原因是它甚至可以在多个客户端同时插入时工作。如果您检查然后插入,则必须在此期间保持锁定,或者无论如何都要预期错误。
这个代码就像
BEGIN
INSERT INTO table( name, age )
VALUES( 'johnny', null );
EXCEPTION
WHEN dup_val_on_index
THEN
NULL; -- Intentionally ignore duplicates
END;
答案 3 :(得分:9)
使用@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回答。
答案 4 :(得分:8)
我发现这些示例在您希望确保目标表中存在行的情况下有点棘手(特别是当您有两列作为主键时),但主键可能根本不存在所以没有什么可以选择的。
这对我有用:
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'
)
关键点是:
SELECT
块中的USING
语句必须始终返回行。如果此查询没有返回任何行,则不会插入或更新任何行。在这里,我从DUAL
中选择,因此总会有一行。ON
条件设置匹配行的条件。如果ON
没有匹配,则运行INSERT语句。WHEN MATCHED THEN UPDATE
子句。答案 5 :(得分:3)
除了目前为止给出的完美有效的答案之外,还有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上描述。
答案 6 :(得分:2)
您可以使用以下语法:
INSERT INTO table_name ( name, age )
select 'jonny', 18 from dual
where not exists(select 1 from table_name where name = 'jonny');
如果它打开一个要求“输入替换变量”的pop,那么在上述查询之前使用它:
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');
答案 7 :(得分:0)
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 "关于类固醇。所以,这对我和我大多数都有帮助。
答案 8 :(得分:0)
您应使用“合并”: 例如:
MERGE INTO employees e
USING (SELECT * FROM hr_records WHERE start_date > ADD_MONTHS(SYSDATE, -1)) h
ON (e.id = h.emp_id)
WHEN MATCHED THEN
UPDATE SET e.address = h.address
WHEN NOT MATCHED THEN
INSERT (id, address)
VALUES (h.emp_id, h.address);
或
MERGE INTO employees e
USING hr_records h
ON (e.id = h.emp_id)
WHEN MATCHED THEN
UPDATE SET e.address = h.address
WHEN NOT MATCHED THEN
INSERT (id, address)
VALUES (h.emp_id, h.address);