Oracle: как вставить, если строка не существует - PullRequest
46 голосов
/ 01 октября 2010

Какой самый простой способ вставить строку, если она не существует, в PL / SQL (оракул)?

Я хочу что-то вроде:

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

Но это не работает.

Примечание: эта таблица имеет 2 поля, скажем, имя и возраст . Но только имя является PK.

Ответы [ 8 ]

66 голосов
/ 01 октября 2010
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'
                  )
32 голосов
/ 01 октября 2010

Если вы используете 10g, вы также можете использовать оператор MERGE. Это позволяет вам вставить строку, если она не существует, и игнорировать строку, если она существует. Люди склонны думать о MERGE, когда хотят сделать «upsert» (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
14 голосов
/ 01 октября 2010

Если имя PK, просто вставьте и поймайте ошибку. Причина, по которой это делается, а не любая проверка, заключается в том, что он будет работать даже при одновременной вставке нескольких клиентов. Если вы проверяете, а затем вставляете, вы должны удерживать блокировку в течение этого времени или в любом случае ожидать ошибку.

Код для этого будет что-то вроде

BEGIN
  INSERT INTO table( name, age )
    VALUES( 'johnny', null );
EXCEPTION
  WHEN dup_val_on_index
  THEN
    NULL; -- Intentionally ignore duplicates
END;
9 голосов
/ 01 октября 2010

Используя части ответа @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.

8 голосов
/ 22 января 2015

Мне показалось, что примеры несколько сложны для ситуации, когда вы хотите убедиться, что в таблице назначения существует строка (особенно, когда у вас есть два столбца в качестве первичного ключа), но первичного ключа там вообще не может быть так что нечего выбирать.

Вот что у меня сработало:

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, если вы также хотите больше контролировать обновления.
3 голосов
/ 09 августа 2013

В дополнение к прекрасным и действительным ответам, данным до сих пор, есть также подсказка 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;

Подсказка описана на Таити .

2 голосов
/ 09 августа 2013

вы можете использовать этот синтаксис:

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');
0 голосов
/ 26 сентября 2016

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

Я называю это " ЕСЛИ НЕ СУЩЕСТВУЕТ " на стероидах. Так что это помогает мне, и я в основном это делаю.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...