Oracle:如果行不存在,如何插入

Oracle:如果行不存在,如何插入,oracle,plsql,Oracle,Plsql,在PL/SQL oracle中,插入不存在的行的最简单方法是什么 我想要像这样的东西: IF NOT EXISTS (SELECT * FROM table WHERE name = 'jonny') THEN INSERT INTO table VALUES ("jonny", null); END IF; 但它不起作用 注意:此表有两个字段,例如名称和年龄。但唯一的名称是PK。如果名称是PK,则只需插入并捕获错误。这样做而不是进行任何检查的原因是,即使多个客户端同时插入,它也可以工作。

在PL/SQL oracle中,插入不存在的行的最简单方法是什么

我想要像这样的东西:

IF NOT EXISTS (SELECT * FROM table WHERE name = 'jonny') THEN
  INSERT INTO table VALUES ("jonny", null);
END IF;
但它不起作用


注意:此表有两个字段,例如名称和年龄。但唯一的名称是PK。

如果名称是PK,则只需插入并捕获错误。这样做而不是进行任何检查的原因是,即使多个客户端同时插入,它也可以工作。如果选中然后插入,则在此期间必须保持锁定,否则将出现错误

这方面的代码类似于

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

假设您使用的是10g,也可以使用MERGE语句。这允许您插入不存在的行,并忽略存在的行。当人们想要在行不存在时执行upsert插入,如果行确实存在时进行更新,但更新部分现在是可选的,因此也可以在这里使用它时,他们倾向于考虑合并

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

使用@benoit answer的部分内容,我将使用以下内容:

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检查,因为我的代码比这个带有name和age字段的示例表复杂得多。我需要一个非常清晰的代码。谢谢,我学到了很多!我将接受@benoit-answer。

您可以使用以下语法:

如果它打开了一个pop请求作为enter替换变量,则在进行上述查询之前使用此选项:

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

除了到目前为止给出的完美有效的答案外,还有您可能希望使用的忽略\u行\u on\u dupkey\u索引提示:

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;

上面介绍了提示。

我发现下面的示例有点棘手,因为您希望确保目标表中存在一行,特别是当您有两列作为主键时,但主键可能根本不存在,因此没有什么可选择的

这就是我的工作原理:

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

-

-

如果不存在类固醇,我就称之为。因此,这对我和我都很有帮助。

您应该使用合并: 例如:

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


代码:开始插入表值'jonny',null;sqlcode!=-1然后提出;完;sqlcode=-1 ORA-00001时是否有意义尝试插入并捕获异常取决于您期望插入成功的频率。如果99%的时间插入一个非重复值,并且只有1%的时间会出错,那么捕获并忽略异常是一个不错的选择。如果99%的时间行已经存在,从性能角度来看捕获异常可能会有问题。。。选择不需要的。我知道OP以单行为例,但对于这一点以及Justin Cave提出的性能问题,我认为合并是更好的解决方案。您是否希望INSERT语句通常是必需的,即该行通常不存在?或者这一行通常会存在?@justin:这一行通常不会存在。酷。那么这三个选项中的任何一个都应该适合您。@Jeff Walker:dual是Oracle中的一个虚拟表,有一列一行。在SQLite中,这是不好的,您可以不从中选择,在Oracle中,您必须使用dual来选择。1当多个会话同时尝试插入同一行时,这将不起作用。在提交之前,这两个会话都不会看到其他会话的数据,此时为时已晚。最好的解决方案是应用唯一的约束。最新评论:@JeffreyKemp,有些用例不需要担心同时会话。@Justinskilles,这些用例是特殊的,与这个问题无关。当设计师决定DBMS的一个基本特性(在这种情况下)是并发性时,就不必担心了,这正是他们的客户日后会遇到的问题,很有可能。这与三年前发布的公认答案有什么不同?选择MAXid作为报告中的id将不适用于并发事务您能提供一些信息吗?我只是不太了解所有的事情。
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
      ));
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);