Ваш код не работает так, как вы думаете.
В вашей инструкции вставки выбирается все строки, которые не соответствуют входному параметру 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;
Это лучшее решение, потому что:
- с использованием последовательности более масштабируема, чем выбор
Max(m.ID_MIEJSCOWOSCI)+1,1)
- с использованием одного заявления работает лучше в многопользовательской среде
Проверка наличия несуществующего значения в таблице всегда проблематична. Если две сессии проверяют одно и то же NM
, то одновременно и не найдут его (из-за уровня изоляции фиксации чтения Oracle): следовательно, оба будут вставлять записи для одного и того же значения. Единственный безопасный способ предотвратить этот сценарий - установить ограничение UNIQUE для рассматриваемого столбца. (Это также масштабируется лучше, чем блокировка всей таблицы в эксклюзивном режиме.)
Спасибо. Похоже, мне нужно выяснить инструкцию IF, чтобы не вставлять строку, когда она существует – mallorn
Мой плохой, я не видел, что seq уже создан учителем – mallorn
Этот код по-прежнему не прав - подумайте, что может произойти в многопользовательской среде, когда два процессы пытаются вставить запись в одно и то же время. Это все равно будет «повторяться ошибка ключа» время от времени. – krokodilko