ORACLE:如果不存在则插入行 - 重复键错误

时间:2016-01-17 21:18:47

标签: sql oracle composite-primary-key

有很多类似的问题,但我需要编写代码。

我知道,作为一种好习惯,我应该使用NAZWA代替ID,但我的作业是编写程序,而不是创建新序列。

如果MIEJSCOWOSC没有重复,我需要插入新行,但必须增加ID_MIEJSCOWOSCI

NAZWA有两列( PK SELECT NVL(Max(m.ID_MIEJSCOWOSCI)+1,1) INTO mID为NUMBER,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; / 为VARCHAR2)

我很清楚我的序列计数器      function createEvent() { var calendarId = 'primary'; var start = getRelativeDate(1, 12); var end = getRelativeDate(1, 13); var event = { summary: 'Lunch Meeting', location: 'The Deli', description: 'To discuss our plans for the presentation next week.', start: { dateTime: start.toISOString() }, end: { dateTime: end.toISOString() }, attendees: [{ email: 'alice@example.com' }, { email: 'bob@example.com' }], attachments: [{ fileId: "1sGm4o0DVJFjJQun_oj1PWD9MuFCiikqamM7B0TkwH6w", }, ], colorId: 9 }; event = Calendar.Events.insert(event, calendarId); Logger.log('Event ID: ' + event.getId()); } function getRelativeDate(daysOffset, hour) { var date = new Date(); date.setDate(date.getDate() + daysOffset); date.setHours(hour); date.setMinutes(0); date.setSeconds(0); date.setMilliseconds(0); return date; } 我收到的时候工作不正常:

  

UPDATE或INSERT语句尝试插入重复键。

我该如何修复此代码?

'

1 个答案:

答案 0 :(得分:3)

您的代码没有按照您的想法执行。

您的insert语句选择与输入参数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. 更具可扩展性
  3. 使用单个语句在多用户环境中效果更好
  4. 检查表中是否存在值总是有问题的。如果两个会话同时检查相同的NM,则不会找到它(因为Oracle的读取提交隔离级别):因此两者都将插入相同值的记录。防止这种情况的唯一安全方法是在相关列上放置UNIQUE约束。 (这也比在独占模式下锁定整个表格更好。)