2017-10-09 73 views
0

我创建了一个表,一个序列和一个触发器。我用正确的数据添加第一条记录。在ID列中,我有1.然后我尝试添加无效数据的记录,我得到一个错误。正确的错误,将数据添加到表格。不幸的是,新记录ID = 3。为什么会发生这种情况?无论尝试输入无效数据的次数如何,我都会在ID列中始终有连续的数字。如何创建正确的工作顺序?

--CREATING TABLE TEST 
CREATE TABLE TEST_TABLE 
    ("ID" NUMBER PRIMARY KEY NOT NULL, 
    "NAME" VARCHAR2(20) NOT NULL, 
    "PERSONAL_ID_NUM" VARCHAR(11) UNIQUE NOT NULL, 
    CONSTRAINT "LENGTH_PIN" CHECK (LENGTH(PERSONAL_ID_NUMBER) = 11), 
    CONSTRAINT "IS_DIGIT_PIN" CHECK (REGEXP_LIKE(P_PESEL2,'^[0-9]*$')) 
    ); 

--CREATING SEQUENCE 
CREATE SEQUENCE SEQ_TEST_TABLE START WITH 1 INCREMENT BY 1 MINVALUE 1 NOMAXVALUE NOCYCLE CACHE 2; 

--CREATING TRIGGER 
CREATE OR REPLACE TRIGGER TR_TEST_TABLE 
BEFORE INSERT ON TEST_TABLE FOR EACH ROW 
BEGIN 
SELECT SEQ_TEST_TABLE.NEXTVAL 
INTO :new.ID 
FROM dual; 
END; 

--INSERT DATA, VALID VALUES 
INSERT INTO TEST_TABLE(ID, NAME, PERSONAL_ID_NUM) 
VALUES(SEQ_TEST_TABLE.NEXTVAL, 'JOHN', '12345678901'); 

--CHECK TABLE 
SELECT * FROM TEST_TABLE; 

--TRYING INSERT NOT VALID VALUES 
INSERT INTO TEST_TABLE(ID, NAME, PERSONAL_ID_NUM) 
VALUES(SEQ_TEST_TABLE.NEXTVAL, 'EMIL', '1234567890A'); 

--INSERT VALID VALUES 
INSERT INTO TEST_TABLE(ID, NAME, PERSONAL_ID_NUM) 
VALUES(SEQ_TEST_TABLE.NEXTVAL, 'EMIL', '12345678902'); 

--CHECK TABLE (NEW ROW HAVE ID = 3) 
SELECT * FROM TEST_TABLE; 
+0

当ID col是主键时,为什么要用Sequence值填充此值。触发器执行Seq值,当它失败时,下一个值将被插入到表中。不要使用Seq。只使用主键来实现这一点。 –

+3

使用序列连续编号几乎是不可能的 - 无论你怎么想,你都不需要它们在数据库中!考虑回滚 - 每次生成下一个序列号时,都会发生一些错误,该数字会丢失。所以在序列号中有“漏洞”是完全正确的。一旦你得到了报告(或其他),你可以用任何你想要的方式生成数字。 –

回答

1

不幸的是,新的记录具有ID = 3,这究竟是为什么?

您正在做第一次插入:

INSERT INTO TEST_TABLE(ID, NAME, PERSONAL_ID_NUM) 
VALUES(SEQ_TEST_TABLE.NEXTVAL, 'JOHN', '12345678901'); 

它是从序列获取下一个值1,然后触发运行,并取代了ID值从序列的下一个值2

重复下一个插入,它将得到3,然后触发器将用4替换它。所以你为每个插入使用两个序列值。

你想要做的不是使用原始插入中的序列,而是让触发器提供该值(或者不使用触发器,并在DML语句中恰当地使用SEQ_TEST_TABLE.NEXTVAL,但不能同时使用):

SQL Fiddle

的Oracle 11g R2架构设置

CREATE TABLE TEST_TABLE 
    ("ID" NUMBER PRIMARY KEY NOT NULL, 
    "NAME" VARCHAR2(20) NOT NULL, 
    "PERSONAL_ID_NUM" VARCHAR(11) UNIQUE NOT NULL, 
    CONSTRAINT "LENGTH_PIN" CHECK (LENGTH(PERSONAL_ID_NUM) = 11) 
    ) 
/

CREATE SEQUENCE SEQ_TEST_TABLE START WITH 1 INCREMENT BY 1 MINVALUE 1 NOMAXVALUE NOCYCLE CACHE 2 
/

--CREATING TRIGGER 
CREATE OR REPLACE TRIGGER TR_TEST_TABLE 
BEFORE INSERT ON TEST_TABLE FOR EACH ROW 
BEGIN 
    :new.ID := SEQ_TEST_TABLE.NEXTVAL; 
END; 
/

INSERT INTO TEST_TABLE(NAME, PERSONAL_ID_NUM) 
VALUES('JOHN', '12345678901') 
/

INSERT INTO TEST_TABLE(NAME, PERSONAL_ID_NUM) 
VALUES('EMIL', '1234567890A') 
/

INSERT INTO TEST_TABLE(NAME, PERSONAL_ID_NUM) 
VALUES('EMIL', '12345678902') 
/

查询1

SELECT * FROM TEST_TABLE 

Results

| ID | NAME | PERSONAL_ID_NUM | 
|----|------|-----------------| 
| 1 | JOHN |  12345678901 | 
| 2 | EMIL |  1234567890A | 
| 3 | EMIL |  12345678902 | 
+1

你不需要触发器内的'select'。 ':new.id:= SEQ_TEST_TABLE.NEXTVAL'将正常工作 –

+0

@a_horse_with_no_name真的,我刚刚复制了OP的代码并保持不变 - 现在更新它以避免上下文切换。 – MT0

+0

@ MT0谢谢。现在看起来很明显。 – viehoo

0

从Oracle版本12C 1版中,标识列可被用于自动生成序列。看看这个文件

https://oracle-base.com/articles/12c/identity-columns-in-oracle-12cr1

基本上,格式为(您表):

CREATE TABLE TEST_TABLE 
    ("ID" NUMBER GENERATED ALWAYS AS IDENTITY, 
"NAME" VARCHAR2(20) NOT NULL, 
"PERSONAL_ID_NUM" VARCHAR(11) UNIQUE NOT NULL, 
CONSTRAINT "TEST_PK" PRIMARY_KEY("ID"), 
CONSTRAINT "LENGTH_PIN" CHECK (LENGTH(PERSONAL_ID_NUMBER) = 11), 
CONSTRAINT "IS_DIGIT_PIN" CHECK (REGEXP_LIKE(P_PESEL2,'^[0-9]*$')) 
); 

当您插入,你只是不中的id值:

INSERT INTO TEST_TABLE(NAME, PERSONAL_ID_NUM) 
VALUES('JOHN', '12345678901'); 

该文件指出以下关于标识栏的性能

毫不奇怪,基于触发的测试比其他测试表现得差得多。序列和12c标识列的直接使用提供了类似的结果,这通常比使用触发器填充ID列快一个数量级。

注意 - 和更新:

您也提到这一点很重要的是,ID有没有间隙。如果这是一个困难的要求,那么序列和标识列(内部使用序列)不能保证。您可以通过为您的序列添加NO CACHE来最大限度地减少差距。

然而,guaratee不存在任何差距,你需要使用一个系列化,线程安全的机制,像

  • 具有带有单一记录表(持最后号码)后锁定,增量分配一个号码,然后提交整个事务的一部分。然后,这会对性能产生影响,尤其是在长时间运行事务时
  • 使用包含所有ID(可逐块使用)的表,标记为“挂起”,然后“使用”(两次更新和一个短锁)。这也会影响性能,取决于ID的消费频率
+0

其实。但我保证在脚手架的另一个版本中,在重写期间我必须将其删除。 – viehoo

+0

有用的信息,但不回答问题。 –