Làm thế nào trình tự.nextval có thể là null trong Oracle?


11

Tôi có một chuỗi Oracle được định nghĩa như vậy:

CREATE SEQUENCE  "DALLAS"."X_SEQ"  
    MINVALUE 0 
    MAXVALUE 999999999999999999999999999 
    INCREMENT BY 1 START WITH 0 NOCACHE  NOORDER  NOCYCLE ;

Nó được sử dụng trong một thủ tục được lưu trữ để chèn một bản ghi:

PROCEDURE Insert_Record
                (p_name    IN  VARCHAR2,                
                 p_userid  IN  INTEGER,
                 cur_out   OUT TYPES_PKG.RefCursor)
    IS
        v_id NUMBER := 0;
    BEGIN
        -- Get id value from sequence
        SELECT x_seq.nextval
          INTO v_id
          FROM dual;

        -- Line below is X_PKG line 40
        INSERT INTO X
            (the_id,            
             name,                        
             update_userid)
          VALUES
            (v_id,
             p_name,                        
             p_userid);

        -- Return new id
        OPEN cur_out FOR
            SELECT v_id the_id
              FROM dual;
    END;

Đôi khi, quy trình này trả về lỗi khi được thực thi từ mã ứng dụng.

ORA-01400: cannot insert NULL into ("DALLAS"."X"."THE_ID") 
ORA-06512: at "DALLAS.X_PKG", line 40 
ORA-06512: at line 1

Các chi tiết có thể có hoặc không có liên quan:

  • Phiên bản doanh nghiệp cơ sở dữ liệu Oracle 11g Phiên bản 11.2.0.1.0 - Sản xuất 64 bit
  • Quy trình này được thực thi thông qua Microsoft.Practices. EntrypriseL Library - Data.Oracle.OracleDatabase.ExecuteReader (lệnh DbCommand)
  • Ứng dụng này không bao gồm cuộc gọi trong một giao dịch rõ ràng.
  • Việc chèn không liên tục - dưới 1%

Trong hoàn cảnh nào có thể x_seq.nextvallà null?


Có bao nhiêu mã nằm giữa phần chọn & chèn? Có bất kỳ khối BEGIN..END hoặc bất kỳ câu lệnh EXCEPTION nào trong mã đó không? Là v_id được tham chiếu ở tất cả trong mã đó? Có vẻ hơi lạ. Bạn có thể đặt khối "IF v_id IS NULL THEN .... END IF" ngay sau câu lệnh và để lại một số đầu ra gỡ lỗi ở đâu đó nếu chuỗi trên thực tế gán null cho v_id không? Điều đó hoặc bao bọc chuỗi chọn trong một khối BEGIN..EXCEPTION, vì có thể có một cái gì đó xảy ra mà không được bắt. Một điều cuối cùng - có một kích hoạt trên bàn mà bạn đang chèn vào có thể gây ra nó không?
Philᵀᴹ

@Phil - Việc chọn là ngay trước khi chèn. Không có BEGIN, END hoặc EXCEPTION nào khác ngoài Proc BEGIN / END. v_idchỉ được tham chiếu trong chuỗi chọn, chèn và con trỏ cuối cùng. Bước tiếp theo của chúng tôi là thêm mã gỡ lỗi. Chúng tôi có thể phải chờ kết quả vì nó chỉ xảy ra trong sản xuất và rất không thường xuyên. Có một kích hoạt chèn vào một bảng kiểm toán. Tôi đã chải qua nó mà không có súng khói. Vấn đề đôi khi cũng xảy ra trong các bảng khác mà không có kích hoạt. Cảm ơn đã dành một cái nhìn.
Corbin ngày

5
Điều duy nhất tôi thực sự có thể nghĩ đến vào lúc này là: new.the_id bằng cách nào đó sẽ trở thành NULL trong trình kích hoạt trên bàn X.
Philᵀᴹ

@Phil: đây chắc chắn là nguyên nhân của vấn đề. Bạn nên làm cho nó một câu trả lời.
René Nyffalanger

@ RenéNyffalanger - vấn đề cũng xảy ra trong các procs chèn vào bảng mà không có trình kích hoạt. Nó dường như là một lỗi cơ hội bình đẳng.
Corbin ngày

Câu trả lời:


4

Tôi khá chắc chắn rằng điều này cuối cùng sẽ là một tạo phẩm của mã của bạn hoặc trình điều khiển .net bạn đang sử dụng. Tôi đã giới thiệu một bản demo nhanh cho bạn bằng cách sử dụng SQL thuần túy - PL / SQL và không bao giờ bị mất giá trị chuỗi. Ngẫu nhiên, con trỏ ref bạn đang sử dụng có thể không cần thiết và có khả năng ảnh hưởng đến hiệu suất và khả năng đọc mã - bản demo của tôi bao gồm một quy trình insert_record2 thực hiện nhanh hơn 10% - trong khoảng 26 giây trên máy tính xách tay của tôi so với 36 cho phiên bản con trỏ ref. Tôi ít nhất cũng nghĩ là dễ hiểu hơn. Bạn rõ ràng có thể chạy một phiên bản sửa đổi đối với cơ sở dữ liệu thử nghiệm của bạn với trình kích hoạt kiểm toán.

/* 
demo for dbse 
assumes a user with create table, create sequence, create procedure pivs and quota. 

*/

drop table dbse13142 purge;

create table dbse13142(
    the_id number not null
,   name   varchar2(20)
,   userid number)
;

drop sequence x_seq;
CREATE SEQUENCE  X_SEQ NOCACHE  NOORDER  NOCYCLE ;

create or replace PROCEDURE Insert_Record
                (p_name    IN  VARCHAR2,                
                 p_userid  IN  INTEGER,
                 cur_out   OUT sys_refcursor)
    IS
        v_id NUMBER := 0;
    BEGIN
        -- Get id value from sequence
        SELECT x_seq.nextval
          INTO v_id
          FROM dual;

        -- Line below is X_PKG line 40
        INSERT INTO dbse13142
            (the_id,            
             name,                        
             userid)
          VALUES
            (v_id,
             p_name,                        
             p_userid);

        -- Return new id
        OPEN cur_out FOR
            SELECT v_id the_id
              FROM dual;
    END;
/


create or replace PROCEDURE Insert_Record2
                (p_name    IN  VARCHAR2,                
                 p_userid  IN  INTEGER,
                 p_theid   OUT dbse13142.the_id%type)
    IS
    BEGIN
        -- Get id value from sequence
        SELECT x_seq.nextval
          INTO p_theid
          FROM dual;

        -- Line below is X_PKG line 40
        INSERT INTO dbse13142
            (the_id,            
             name,                        
             userid)
          VALUES
            (p_theid,
             p_name,                        
             p_userid);
    END;
/

set timing on

declare
   c sys_refcursor;
begin   
for i in 1..100000 loop
   insert_record('User '||i,i,c);
   close c;
end loop;
commit;
end;
/

select count(*) from dbse13142;
truncate table dbse13142;

declare
  x number;
begin   
for i in 1..100000 loop
   insert_record2('User '||i,i,x);
end loop;
commit;
end;
/

select count(*) from dbse13142;
truncate table dbse13142;

1
bằng cách một phiên bản với cách tiếp cận truyền thống sử dụng trình kích hoạt cho cột_id và quy trình như sau cũng chạy nhanh hơn tạo hoặc thay thế PROCEDURE Chèn_Record3 (p_name IN dbse13142.name% type, p_userid IN dbse13142.userid% type, p_userid IN dbse13142.userid% type .the_id% type) LÀ BEGIN INSERT INTO dbse13142 (name, userid) VALUES (p_name, p_userid) trả lại the_id vào p_theid; KẾT THÚC; /
Niall Litchfield

Đồng ý rằng đó có thể là sự cố với mã ứng dụng hoặc trình điều khiển. Tôi chỉ tò mò điều gì có thể gây ra giá trị tiếp theo null là tác dụng phụ. Bối rối. Cảm ơn các mẹo hiệu suất. Đó là lời khuyên tốt mà tôi sẽ đề xuất với nhóm.
Corbin ngày

1
Corbin, ý tôi là (và Kevin) là có điều gì đó kỳ lạ đang xảy ra giữa mã của bạn và nhà tiên tri - nếu bạn chạy thử nghiệm hoàn toàn bằng SQL, bạn sẽ không nhận được hiệu quả. Nhưng hãy xem nhận xét của Phil về trình kích hoạt kiểm toán (mà bạn có thể thử vô hiệu hóa).
Niall Litchfield

Tôi hiểu những điểm được thực hiện. Vấn đề tồn tại trong các procs chèn vào các bảng có và không có kích hoạt nên không cần kích hoạt. Khi một kích hoạt tồn tại, nó chỉ cần chèn vào một bảng kiểm toán. Tôi đã xác nhận :new.the_idlà không bị ảnh hưởng. Tôi hiểu câu hỏi của tôi là một cú sút xa. Nó chống lại google-fu của tôi và có vài người gãi đầu ở đây. Tôi chỉ cần hình dung ai đó có thể nhận ra triệu chứng (và điều trị) được cung cấp đủ nhãn cầu. Cảm ơn đã dành một cái nhìn.
Corbin ngày

2

Hãy thử làm một trường hợp thử nghiệm. Tạo một bảng giả và chèn 100.000 bản ghi bằng cách sử dụng trình tự của bạn từ cơ sở dữ liệu. Tôi cá là bạn sẽ không gặp vấn đề gì. Tiếp theo hãy thử chèn điều tương tự từ ứng dụng của bạn.

Điều này có thể được gây ra bởi các vấn đề khác như sự không phù hợp của máy khách Oracle không?

Một giải pháp khác có thể khắc phục sự cố nhưng không thành vấn đề là thêm trình kích hoạt trên bảng.
Trước khi Chèn trên bảng trên Dallas.X NẾU: the_id là null THEN CHỌN x_seq.nextval INTO: the_id TỪ kép; HẾT NẾU;


Tôi không thể tạo lại vấn đề tại địa phương. Nó chỉ xảy ra trong sản xuất và không thường xuyên. Linh cảm của tôi là bạn đúng về máy khách Oracle. Vấn đề đã xuất hiện vài tuần trước trong một bản phát hành mà máy khách không được cập nhật. Tuy nhiên, có cảm giác như có gì đó không hòa hợp giữa ứng dụng và db. Tương tác từ người tiêu dùng khác dường như hoạt động tốt. Kiểm tra null không phải là một ý tưởng tồi, nhưng lý tưởng nhất là tôi muốn tìm hiểu gốc rễ của vấn đề so với công việc xung quanh nó. Ai biết mặc dù? Một công việc xung quanh là tốt hơn so với bị hỏng.
Corbin ngày

0

Tôi chưa có quyền riêng tư để đưa ra nhận xét, vì vậy hãy viết câu này dưới dạng câu trả lời: Vì bạn đang sử dụng phiên bản Oracle> = 11.1, cho phép các chuỗi trong biểu thức PL / SQL thay vì trong SQL, hãy thử điều này:

   v_id := x_seq.nextval;

Thay vì điều này:

 -- Get id value from sequence
    SELECT x_seq.nextval
      INTO v_id
      FROM dual;

Hoặc, mặc dù tôi đã nghe thấy nghi ngờ / cạm bẫy khi sử dụng ".currval", có thể bỏ qua việc gán riêng cho v_id và chỉ sử dụng mã này?:

 -- Line below is X_PKG line 40
        INSERT INTO X
            (the_id,            
             name,                        
             update_userid)
          VALUES
            (x_seq.nextval,
             p_name,                        
             p_userid);

        -- Return new id
        OPEN cur_out FOR
            SELECT x_seq.currval the_id
              FROM dual;

Xin lỗi, hiện tại tôi không có ví dụ 11g để dùng thử.


nó chắc chắn không làm nên sự khác biệt Tôi sử dụng select into...trong 11 nhiều như trong 9i và 10g. Lợi ích duy nhất từ ​​11+ là có thể tham chiếu rõ ràng như bạn đã chỉ ra.
Ben
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.