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

Top*_*era 49 oracle plsql

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

我想要的东西:

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

但它不起作用.

注意:此表有2个字段,例如姓名年龄.但只有名字是PK.

Ben*_*oit 69

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'
                  )
Run Code Online (Sandbox Code Playgroud)

  • dual是Oracle中的一个虚拟表,有一列和一行.这很糟糕(在SQLite中,你可以选择不使用,在Oracle中,你必须在从无处选择时使用双重). (10认同)
  • -1当多个会话尝试同时插入同一行时,这将不起作用 - 在提交之前都不会看到其他会话的数据,此时为时已晚.最佳解决方案是应用唯一约束. (9认同)
  • 迟到评论:@JeffreyKemp,有些用例不需要担心同时发生的会话. (3认同)
  • @JustinSkiles,这些用例是特殊情况,与此问题无关.当设计师决定DBMS的基本功能(在这种情况下,并发性)不会"担心"时,这很可能会在以后咬他们的客户. (3认同)

Jus*_*ave 35

假设你使用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
Run Code Online (Sandbox Code Playgroud)


Lou*_*nco 17

如果name是PK,那么只需插入并捕获错误.这样做而不是任何检查的原因是它甚至可以在多个客户端同时插入时工作.如果您检查然后插入,则必须在此期间保持锁定,或者无论如何都要预期错误.

这个代码就是这样的

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

  • 尝试插入和捕获异常是否有意义取决于您期望INSERT成功的频率.如果99%的时间你插入一个非重复的值并且它只会在1%的时间内出错,那么捕获并忽略异常是一个不错的选择.如果该行已经存在99%的时间,从性能角度来看捕获异常可能会有问题. (3认同)

Top*_*era 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;
Run Code Online (Sandbox Code Playgroud)

很抱歉我没有使用任何完整的给定答案,但我需要IF检查,因为我的代码比具有名称和年龄字段的示例表复杂得多.我需要一个非常清晰的代码.谢谢,我学到了很多东西!我会接受@benoit的回答.


Mal*_*ous 9

我发现在你希望确保目标表中存在一行的情况下,这些例子有点棘手(特别是当你有两列作为主键时),但主键可能根本不存在,所以没有什么选择.

这对我有用:

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'
    )
Run Code Online (Sandbox Code Playgroud)

关键点是:

  • SELECT内部语句USING块必须总是返回行.如果此查询没有返回任何行,则不会插入或更新任何行.在这里我选择,DUAL所以总会有一行.
  • ON条件是什么套用于匹配行的条件.如果ON没有匹配,则运行INSERT语句.
  • WHEN MATCHED THEN UPDATE如果您想要更多地控制更新,也可以添加子句.


归档时间:

查看次数:

110107 次

最近记录:

5 年,11 月 前