Избегайте вставки в оракул [дубликат]

В Ubuntu 16.04 по умолчанию версия PHP 7.0, если вы хотите использовать другую версию, вам необходимо установить пакет PHP в соответствии с версией PHP:

  • PHP 7.2: sudo apt-get install php7.2-curl
  • PHP 7.1: sudo apt-get install php7.1-curl
  • PHP 7.0: sudo apt-get install php7.0-curl
  • PHP 5.6: sudo apt-get install php5.6-curl
  • PHP 5.5: sudo apt-get install php5.5-curl
40
задан cwallenpoole 9 November 2009 в 19:27
поделиться

10 ответов

Вызов называется MERGE. Посмотрите, я слишком ленив.

Остерегайтесь, однако, что MERGE не является атомарным, что может вызвать следующий эффект (спасибо, Marius):

SESS1:

create table t1 (pk int primary key, i int);
create table t11 (pk int primary key, i int);
insert into t1 values(1, 1);
insert into t11 values(2, 21);
insert into t11 values(3, 31);
commit;

SESS2: insert into t1 values(2, 2);

SESS1:

MERGE INTO t1 d
USING t11 s ON (d.pk = s.pk)
WHEN NOT MATCHED THEN INSERT (d.pk, d.i) VALUES (s.pk, s.i);

SESS2: commit;

SESS1: ORA-00001

23
ответ дан erikkallen 24 August 2018 в 01:03
поделиться
  • 1
    Опять же, без блокировки таблицы (или первой записи), есть гонка. Этот метод всегда требует использования таблицы temp. Я бы не сказал, что это неправильно, но иногда может быть слишком много. – Marius Burz 9 November 2009 в 20:45
  • 2
    Нет, слияние должно быть атомарным. – erikkallen 9 November 2009 в 21:07
  • 3
    Да, сам MERGE является атомарным. Но ... Sess1: INSERT pk = 1 INTO myFoo; Sess2: MERGE INTO myFoo d ИСПОЛЬЗОВАНИЕ tmpTable s ON (d.pk = s.pk) ... Sess1: COMMIT; Sess2: ORA-00001; В случаях, когда количество вставленных строк низкое, на самом деле не имеет смысла использовать временную таблицу. Все имеет свою цену, а CREATE TABLE и MERGE не дешевы (посмотрите на необходимые защелки / замки и т. П.). – Marius Burz 9 November 2009 в 22:08
  • 4
    Вам не нужна временная таблица. Если у вас есть только несколько строк, (SELECT 1 FROM dual UNION SELECT 2 FROM dual). Почему ваш пример даст ORA-0001? Не объединил бы блокировку обновления на ключе индекса и не продолжил бы до тех пор, пока Sess1 не вернется или не вернется? – erikkallen 9 November 2009 в 22:37
  • 5
    Эрик, пожалуйста, см. Ответ ниже. Недостаточно места для публикации в качестве комментария, а также никакого форматирования. – Marius Burz 9 November 2009 в 23:17
DECLARE
   tmp NUMBER(3,1);
BEGIN
  SELECT COUNT(content_id) INTO tmp FROM contents WHERE (condition);
  if tmp != 0 then
    INSERT INTO contents VALUES (...);
  else
    INSERT INTO contents VALUES (...);
  end if;
END;

Я использовал приведенный выше код. Это длинный, но простой и сработавший для меня. Аналогично, с кодом Мишеля.

3
ответ дан askmish 24 August 2018 в 01:03
поделиться

Это только вставляет, если элемент, который нужно вставить, еще не присутствует.

Работает так же, как:

if not exists (...) insert ... 

в T-SQL

insert into destination (DESTINATIONABBREV) 
  select 'xyz' from dual 
  left outer join destination d on d.destinationabbrev = 'xyz' 
  where d.destinationid is null;

может быть не очень красивым, но это удобно:)

17
ответ дан Kirk Woll 24 August 2018 в 01:03
поделиться
  • 1
    Или похоже, существует: insert into destination select 'id', 'xyz' from dual where not exists (select id from destination where id = 'id') – robinst 14 June 2012 в 15:32
  • 2
    Решение Robinst (комментарий) является лучшим на мой взгляд – Stijn Geukens 13 January 2016 в 18:18
  • 3
    хороший robinst ... отлично! большое спасибо – KingRider 1 December 2016 в 18:32
  • 4
    code, если не существует (...) insert ... code Ошибка (22,16): PLS-00204: функция или псевдо-столбец «EXISTS» могут использоваться только внутри оператора SQL – Maxple 31 January 2018 в 15:39
  • 5
    Я думаю, что решение robinst не является атомарным, если вам это нужно – deFreitas 16 April 2018 в 23:26

Это ответ на комментарий, отправленный erikkallen:

Вам не нужна временная таблица. Если у вас есть только несколько строк, (SELECT 1 FROM dual UNION SELECT 2 FROM dual). Почему ваш пример даст ORA-0001? Не объединил бы блокировку обновления на ключе индекса и не продолжил бы до тех пор, пока Sess1 не вернется или не вернется? - erikkallen

Ну, попробуйте сами и скажите, есть ли у вас такая же ошибка:

SESS1:

create table t1 (pk int primary key, i int);
create table t11 (pk int primary key, i int);
insert into t1 values(1, 1);
insert into t11 values(2, 21);
insert into t11 values(3, 31);
commit;

SESS2 : insert into t1 values(2, 2);

SESS1:

MERGE INTO t1 d
USING t11 s ON (d.pk = s.pk)
WHEN NOT MATCHED THEN INSERT (d.pk, d.i) VALUES (s.pk, s.i);

SESS2: commit;

SESS1: ORA-00001

0
ответ дан Marius Burz 24 August 2018 в 01:03
поделиться
  • 1
    У меня нет доступа к Oracle, поэтому я не могу попробовать, но я верю вам. Я действительно думаю, что это следует считать ошибкой. – erikkallen 10 November 2009 в 10:15
  • 2
    это правильное поведение по уровню считываемой транзакции, а не к ошибке. поведение MERGE полностью согласуется с поведением обновления, которое не влияет на строки, за которыми следует попытка вставки. – David Aldridge 10 November 2009 в 11:07
  • 3
    @David: Я понимаю, что эти вещи эквивалентны, но мне интересно, сколько людей это знают. Я уверен, что нет, и я действительно ожидал, что он будет работать без проблем. Если я хочу семантику INSERT, которая не вставляет строки, то UPDATE, тогда я пишу INSERT, а затем UPDATE. – erikkallen 10 November 2009 в 11:20
  • 4
    И почему -1 для этого? Это (по-видимому) правильно, и это научило меня чему-то. +1. – erikkallen 10 November 2009 в 11:20
  • 5
    -1, потому что он не отвечает на вопрос, хотя я согласен, что это интересная информация. Вы можете добавить информацию в свой ответ или создать свою вики сообщества, чтобы другие могли. Также это, безусловно, не следует рассматривать как ошибку. – David Aldridge 10 November 2009 в 12:57

Этот код находится на клиенте, тогда у вас много поездок на сервер, чтобы устранить это.

Вставьте все данные во временную таблицу, скажем, T с той же структурой, что и myFoo

Тогда

insert myFoo
  select *
     from t
       where t.primary_key not in ( select primary_key from myFoo) 

Это должно работать и с другими базами данных - я сделал это на Sybase

. Не самое лучшее, если очень мало новых данных для если вы скопировали все данные по проводу.

5
ответ дан Mark 24 August 2018 в 01:03
поделиться
  • 1
    Определенно кливер +1. Сначала мне нужно создать временную таблицу, но это действительно не страшная трудность. – cwallenpoole 9 November 2009 в 20:20
  • 2
    коррекция умный . – cwallenpoole 9 November 2009 в 20:26
  • 3
    вставка этого способа SLOW .... BULK COLLECT - это намного лучший вариант ... google it :) Это требует некоторого pl-sql, но он бесконечно быстрее, чем слепая вставка из select. – Matt 27 May 2012 в 04:31

Поздняя вечеринка, но ...

С помощью oracle 11.2.0.1 есть семантический намек , который может это сделать: IGNORE_ROW_ON_DUPKEY_INDEX

Пример:

insert /*+ IGNORE_ROW_ON_DUPKEY_INDEX(customer_orders,pk_customer_orders) */
  into customer_orders
       (order_id, customer, product)
values (    1234,     9876,  'K598')
     ;

UPDATE: Хотя этот подсказку работает (если вы правильно произносите его), существуют лучшие подходы , которые не требуют Oracle 11R2:

Первый подход: прямой перевод вышеизложенного семантического намека:

begin
  insert into customer_orders
         (order_id, customer, product)
  values (    1234,     9876,  'K698')
  ;
  commit;
exception
  when DUP_VAL_ON_INDEX
  then ROLLBACK;
end;

Второй aproach & mdash; lot быстрее, чем оба вышеупомянутых намека, когда есть много разногласий:

begin
    select count (*)
    into   l_is_matching_row
    from   customer_orders
    where  order_id = 1234
    ;

    if (l_is_matching_row = 0)
    then
      insert into customer_orders
             (order_id, customer, product)
      values (    1234,     9876,  'K698')
      ;
      commit;
    end if;
exception
  when DUP_VAL_ON_INDEX
  then ROLLBACK;
end;
35
ответ дан Michael Deardeuff 24 August 2018 в 01:03
поделиться
  • 1
    Мне нравится второй подход, потому что ясно и легко понять, что он пытается сделать. – Diego Deberdt 30 September 2011 в 10:04
  • 2
    Ища альтернативу тому, как я решал эту ситуацию, я наткнулся на ваш ответ, который подтверждает, что мой подход был правильным. Я реализовал второй подход, и это ясно и быстро! +1 – user 10 February 2015 в 08:48
  • 3
    Совет для тех, кто хочет получить доступ к объектам, созданным через статический SQL (не PL / SQL, который является динамическим SQL). Например, если вы хотите получить NEXTVAL из последовательности, которую вы создали за пределами PL / SQL & quot; BEGIN END; & quot; блок. Do & quot; DECLARE seq_value NUMBER; & quot; и затем «SELECT employees_seq.NEXTVAL INTO seq_value FROM dual; & quot; [Д0] docs.oracle.com/cd/B19306_01/appdev.102/b14261/… – Peeter Kokk 6 July 2017 в 09:47

Если вы НЕ хотите сливаться с другой таблицей, а скорее вставляете новые данные ... Я придумал это. Возможно, лучший способ сделать это?

MERGE INTO TABLE1 a
    USING DUAL
    ON (a.C1_pk= 6)
WHEN NOT MATCHED THEN
    INSERT(C1_pk, C2,C3,C4)
    VALUES (6, 1,0,1);
8
ответ дан Nikita 24 August 2018 в 01:03
поделиться

Мы можем объединить DUAL и NOT EXISTS для архивирования вашего требования:

INSERT INTO schema.myFoo ( 
    primary_key, value1, value2
) 
SELECT
    'bar', 'baz', 'bat' 
FROM DUAL
WHERE NOT EXISTS (
    SELECT 1 
    FROM schema.myFoo
    WHERE primary_key = 'bar'
);
9
ответ дан OnionHead 24 August 2018 в 01:03
поделиться

INSERT INTO schema.myFoo ( primary_key        , value1          , value2         )
                         SELECT 'bar1' AS primary_key ,'baz1' AS value1 ,'bat1' AS value2 FROM DUAL WHERE (SELECT 1 AS value FROM schema.myFoo WHERE LOWER(primary_key) ='bar1' AND ROWNUM=1) is null;

0
ответ дан RaZieRSarE 24 August 2018 в 01:03
поделиться

Если ваша таблица является «независимой» от других (я имею в виду, что она не будет вызывать каскадное удаление или не установит отношения внешних ключей к нулю), хороший трюк может состоять в том, чтобы сначала УДАЛИТЬ строку, а затем снова ВСТАВИТЬ , Это может выглядеть так:

УДАЛИТЬ ОТ MyTable WHERE prop1 = 'aaa'; // предполагая, что он выберет не более одной строки!

INSERT INTO MyTable (prop1, ...) VALUES ('aaa', ...);

Если вы удаляете то, что не существует, ничего не произойдет.

0
ответ дан zetzer 24 August 2018 в 01:03
поделиться
  • 1
    есть ли способ проверить, существует ли какая-либо строка. – Claudio Santos 28 October 2013 в 16:49
Другие вопросы по тегам:

Похожие вопросы: