ORA-02437:“主键被侵犯” - 为什么我在SQL Developer中看不到重复的ID?

时间:2016-08-25 14:40:04

标签: oracle constraints primary-key oracle-sqldeveloper

我会收到错误:

ORA-02437:  cannot validate  (%s.%s) - primary key violated
Cause:  attempted to validate a primary key with duplicate values or null values

我发现这是因为我有一个存储过程会增加ID,但是当它重新运行并且有一个与我的某个数据类型相关的错误时它没有这样做。我发现我的数据库表中现在有一个重复的ID。所有这些都是有道理的,我能够使用DELETE FROM MyTable WHERE ID = x轻松纠正它,其中x是令人讨厌的重复ID。 我遇到的问题是我能够找到重复的ID的唯一方法是因为我做了SELECT * FROM MyTable WHERE ID = x - 其中x是比我实际可以看到的最后一个ID大一个。我只是通过一个有根据的猜测找到它。所以:

  1. 当我在Oracle SQL Developer中打开表时,为什么我看不到这些重复的ID?它仅显示最后一行作为重复项之前的ID。我不认为这是因为我的主键约束,因为我的存储过程中的第一行是删除它(并把它放回去,最后 - 可能是当我收到我的错误时),并且它不存在时我看着我的桌子。
  2. 有没有办法让这些插入表中的最后一个ID可见,所以我不必猜测或假设重复的ID“隐藏”为比我表中的最后一个ID大一个, 在将来?我的存储过程中有一个commit;,所以它们应该出现 - 当然,除非程序在运行该行代码之前挂起(非常可能)。
  3. 运行的存储过程:

    create or replace 
    PROCEDURE PRC_MYTABLE_INTAKE(
    , EMPLOYEE_ID IN NVARCHAR2
    , TITLE_POSITION IN NVARCHAR2
    , CREATED_DATE IN DATE
    , LAST_MODIFIED IN DATE
    ) AS
     myid integer := 0;
     appid integer := 0;
    BEGIN
    -- disable PK constraint so it can be updated
    EXECUTE IMMEDIATE 'ALTER TABLE MYTABLE DROP CONSTRAINT MYTABLE_PK';  
    COMMIT;
    -- assign ID to myid
    SELECT ID INTO myid FROM MYTABLE WHERE ROWID IN (SELECT MAX(ROWID) FROM MYTABLE);
    -- increment
    myid := myid + 1;
    -- assign APPLICATION_ID to appid
    SELECT APPLICATION_ID INTO appid FROM MYTABLE WHERE ROWID IN (SELECT MAX(ROWID) FROM MYTABLE);
    -- increment
    appid := appid + 1;
    -- use these ids to insert with
    INSERT INTO MYTABLE (ID, APPLICATION_ID, 
    , EMPLOYEE_ID
    , TITLE_POSITION
    , CREATED_DATE
    , LAST_MODIFIED
     ) VALUES(myid, appid, 
    , EMPLOYEE_ID
    , TITLE_POSITION
    , CREATED_DATE
    , LAST_MODIFIED
    );
    COMMIT;
    -- re-enable the PK constraint
    EXECUTE IMMEDIATE 'ALTER TABLE PASS ADD CONSTRAINT MYTABLE_PK PRIMARY KEY (ID)';
    COMMIT;
    END;
    

3 个答案:

答案 0 :(得分:1)

这是一个问题:

SELECT ID
INTO myid
FROM MYTABLE
WHERE ROWID IN (SELECT MAX(ROWID) FROM MYTABLE)

ID和ROWID之间没有相关性,所以你没有得到最大的当前ID,你只是得到恰好位于距离数据文件开头最远的行上的那个

您需要的代码是:

SELECT COALESCE(MAX(ID),0)
FROM   MYTABLE;

或者更好的是,只需使用一个序列。

不知道你为什么放弃PK。

此外,当您发出查询时:

SELECT APPLICATION_ID INTO appid ...

...可能与您已获得id的行不同,因为可能已将更改提交给表。

当然另一个问题是你不能同时运行这个程序的两个实例。

答案 1 :(得分:0)

对于大卫·阿尔德里奇来说,既然他想看代码而不是我发布问题的真正原因,请运行---

CREATE TABLE YOURSCHEMA.TESTING 
(
  TEST_ID NVARCHAR2(100) NOT NULL
, TEST_TYPE NVARCHAR2(100) NOT NULL
, CONSTRAINT TEST_PK PRIMARY KEY 
 (
    TEST_ID
 )
 ENABLE 
);

create or replace 
PROCEDURE PRC_TESTING_INSERT(
  TEST_TYPE IN NVARCHAR2
) AS
  testid integer := 0;
BEGIN
-- disable PK constraint so it can be updated
EXECUTE IMMEDIATE 'ALTER TABLE TESTING DROP CONSTRAINT TEST_PK';  
COMMIT;
-- assign TEST_ID to testid
SELECT TEST_ID INTO testid FROM TESTING WHERE ROWID IN (SELECT MAX(ROWID) FROM TESTING);
-- increment
testid := testid + 1;
-- use this id to insert with
INSERT INTO TESTING (TEST_ID, TEST_TYPE) VALUES(testid, TEST_TYPE);
COMMIT;
-- re-enable the PK constraint
EXECUTE IMMEDIATE 'ALTER TABLE TESTING ADD CONSTRAINT TEST_PK PRIMARY KEY (TEST_ID)';
COMMIT;
END;

SET serveroutput on;
DECLARE
    test_type varchar(100);
BEGIN
   test_type := 'dude';
   YOURSCHEMA.PRC_TESTING_INSERT(test_type);
   -- to verify the variable got set and procedure ran, could do:
   --dbms_output.enable;
   --dbms_output.put_line(test_type);
END;

现在,由于表中没有数据,存储过程将失败并显示ORA-06512: no data found。如果您再尝试再次运行它,您将获得ORA-02443: cannot drop constraint - nonexistent constraint,因为EXECUTE IMMEDIATE 'ALTER TABLE TESTING DROP CONSTRAINT TEST_PK';已成功删除它,并且该过程从未在最后运行该命令以重新添加它。这让我觉得我需要提交,但即使没有它们,它仍然无法完成整个过程。

要证明程序运行,如果给出了正确的数据,请在创建表之后但在创建/运行存储过程之前运行:

INSERT INTO TESTING (TEST_ID, TEST_TYPE)
VALUES ('1', 'hi');

如果你从一个新表中运行proc(不是那个删除了约束的表),它就可以运行了。

答案 2 :(得分:-1)

由于mathguy没有将此作为答案发布,但我会相信他的信息......

回答为什么我看不到重复项是因为由于数据类型不匹配而导致失败的程序中没有出现COMMIT(我们发现实际上是在应用程序的代码中发送了变量的值进入这个过程,而不是在存储过程本身)。 (这也是为什么我会记下那些说你不必在这个过程中添加这么多COMMIT行的人。)这些命令是在启动它的用户的会话中运行的 - 在我的例子中,我登录的同一个DB用户的另一个会话,但是从我的应用程序开始,而不是我的SQL Developer会话。它还解释了为什么我可以自己做COMMIT,但它不会影响应用程序的会话 - 我无法提交从另一个会话运行的任何操作。如果COMMIT作为OracleCommand运行,并且在.ExecuteNonQuery申请OracleConnection失败后立即catch WHERE ID =,我会看到SQL Developer中的行,无需进行特殊查询。

因此,简而言之,查看项目的唯一方法是使用 "documents": [{ "type": "Resume", "url": "https://.s3.amazonaws.com/F58723BD-6148-E611-8110-000C29E6C08D.txt" }, { "type": "Reference", "url": "https://.s3.amazonaws.com/F58723BD-6148-E611-8110-000C29E6C08D.txt" }] 进行直接查询,找到最后一个ID并将其递增,并将其放入查询中。

相关问题