2016-01-17 3 views
1

Существовала куча похожих вопросов, но мне нужно написать свой код.ORACLE: строка вставки, если она не существует, - ошибка повторяющегося ключа

Я знаю, что, как хорошая практика, я должен использовать seq.nextval вместо SELECT NVL(Max(..., но моя домашняя работа - написать процедуру, а не создавать новую последовательность.

Мне нужно вставить новую строку, если NAZWA не дублируется, а должен увеличивать ID.

Таблица MIEJSCOWOSC имеет две колонки (PKID_MIEJSCOWOSCI как ЧИСЛА, NAZWA в VARCHAR2)

Это ясно для меня, что мой счетчик последовательности SELECT NVL(Max(m.ID_MIEJSCOWOSCI)+1,1) INTO mID не работает должным образом, как я получаю:

Оператор UPDATE или INSERT попытался вставить дубликат ключа.

Как исправить этот код?

CREATE OR REPLACE PROCEDURE WstawMiejscowosc (
    NM IN Miejscowosc.Nazwa%TYPE) 
AS 
    mID Miejscowosc.ID_MIEJSCOWOSCI%TYPE; 
BEGIN 
    SELECT NVL(Max(m.ID_MIEJSCOWOSCI)+1,1) INTO mID 
    FROM Miejscowosc m; 
    INSERT INTO Miejscowosc m 
    select mID, NM 
    from Miejscowosc 
    where not exists (select 1 from Miejscowosc m where m.Nazwa = NM); 
END; 
/

ответ

3

Ваш код не работает так, как вы думаете.

В вашей инструкции вставки выбирается все строки, которые не соответствуют входному параметру NM. Поэтому, когда в таблице содержится несколько строк, не соответствующих друг другу, ваш оператор попытается вставить столько строк из NM с тем же производным значением для id. Вот почему вы получаете исключение ORA-00001.

Решение: проверьте наличие пройденного значения и вставьте только одну запись, если это значение не найдено. ,

CREATE OR REPLACE PROCEDURE WstawMiejscowosc (
    NM IN Miejscowosc.Nazwa%TYPE) 
AS 
    mID Miejscowosc.ID_MIEJSCOWOSCI%TYPE; 
    x varchar2(1); 
    cursor c_nm_exists (p_nm varchar2) is 
     select null into x 
     from Miejscowosc m 
     WHERE m.Nazwa = NM; 

BEGIN 
    open c_nm_exists(p_nm); 
    fetch c_nm_exists into x; 
    if c_nm_exists%notfound then 
      select Max(m.ID_MIEJSCOWOSCI)+1,1) 
      into mID; 
      INSERT INTO Miejscowosc m 
      values (mID, NM); 
    end if; 
    close c_nm_exists; 
END; 
/

Это неуклюжий кусок кода. Однако это лучше, чем мое предыдущее предложение, поскольку оно позволяет избежать использования исключения для реализации бизнес-логики и обрабатывает переданное значение NM, соответствующее более одной строке. Он по-прежнему имеет несколько выборок на столе, но различные ограничения делают это неизбежным.


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

Раствор с мнимым индексом может выглядеть следующим образом:

CREATE OR REPLACE PROCEDURE WstawMiejscowosc (
    NM IN Miejscowosc.Nazwa%TYPE) 
AS 
    mID Miejscowosc.ID_MIEJSCOWOSCI%TYPE; 
    x varchar2(1); 
    cursor c_nm_exists (p_nm varchar2) is 
     select null into x 
     from Miejscowosc m 
     WHERE m.Nazwa = NM; 

BEGIN 
    open c_nm_exists(p_nm); 
    fetch c_nm_exists into x; 
    if c_nm_exists%notfound then 
      INSERT INTO Miejscowosc m 
      values (m_id_sequence.nextval, NM); 
    end if; 
    close c_nm_exists; 
END; 
/

Хотя это может быть упрощена до MERGE:

CREATE OR REPLACE PROCEDURE WstawMiejscowosc (
    NM IN Miejscowosc.Nazwa%TYPE) 
AS 
begin 
    merge into Miejscowosc m 
    using (select NM from dual) q 
    on (q.NM = m.Nazwa) 
    when not matched then 
      insert values (m_id_sequence.nextval, NM); 
end; 

Это лучшее решение, потому что:

  1. с использованием последовательности более масштабируема, чем выбор Max(m.ID_MIEJSCOWOSCI)+1,1)
  2. с использованием одного заявления работает лучше в многопользовательской среде

Проверка наличия несуществующего значения в таблице всегда проблематична. Если две сессии проверяют одно и то же NM, то одновременно и не найдут его (из-за уровня изоляции фиксации чтения Oracle): следовательно, оба будут вставлять записи для одного и того же значения. Единственный безопасный способ предотвратить этот сценарий - установить ограничение UNIQUE для рассматриваемого столбца. (Это также масштабируется лучше, чем блокировка всей таблицы в эксклюзивном режиме.)

+0

Спасибо. Похоже, мне нужно выяснить инструкцию IF, чтобы не вставлять строку, когда она существует – mallorn

+0

Мой плохой, я не видел, что seq уже создан учителем – mallorn

+0

Этот код по-прежнему не прав - подумайте, что может произойти в многопользовательской среде, когда два процессы пытаются вставить запись в одно и то же время. Это все равно будет «повторяться ошибка ключа» время от времени. – krokodilko

Смежные вопросы