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

时间:2010-10-01 17:04:26

标签: oracle plsql

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

我想要类似的东西:

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

但它不起作用。

注意:此表格包含2个字段,例如名称年龄。但只有名称才是PK。

9 个答案:

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

https://oracle-base.com/articles/9i/merge-statement