Як послідовність.nextval може бути нульовою в Oracle?


11

У мене така послідовність Oracle визначена так:

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

Він використовується в збереженій процедурі для вставки запису:

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;

Інколи ця процедура повертає помилку при виконанні з коду програми.

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

Деталі, які можуть бути або не бути актуальними:

  • Випуск бази даних Oracle 11g Enterprise Edition 11.2.0.1.0 - 64-бітне виробництво
  • Процедура виконується за допомогою Microsoft.Practices.EnterpriseLibrary - Data.Oracle.OracleDatabase.ExecuteReader (команда DbCommand)
  • Додаток не завершує виклик у явній транзакції.
  • Вставка виходить з перервами - менше 1%

За яких обставин може x_seq.nextvalбути недійсним?


Скільки коду між виділенням та вставкою? Чи є в цьому коді блоки BEGIN..END або будь-які заяви EXCEPTION? Чи взагалі посилання на v_id у цьому коді? Здається трохи дивно. Чи можете ви поставити блок "IF v_id IS NULL THEN .... END IF" безпосередньо після оператора і залишити десь налагоджувальний висновок де-небудь, якщо послідовність насправді призначить null v_id? Виберіть цю послідовність або оберніть послідовність у блоці BEGIN..EXCEPTION, оскільки може статися щось, що не було спіймано. Останнє - чи є на столі, який ви вставляєте, тригер, який може викликати його?
Philᵀᴹ

@Phil - Вибір знаходиться безпосередньо перед вставкою. Немає ПОЧАТКУ, КІНЦЯ чи ВИКЛЮЧЕННЯ, окрім pro BEGIN / END. v_idпосилається лише на вибір послідовності, вставку та кінцевий курсор. Наступним нашим кроком було додати код налагодження. Нам, можливо, доведеться чекати результатів, оскільки це відбувається лише у виробництві і дуже рідко. Існує тригер, який вставляється в таблицю аудиту. Я прочесав це через не палючий пістолет. Проблема також періодично виникає в інших таблицях без тригерів. Дякуємо, що подивились.
Корбін березня

5
Єдине, про що я зараз можу подумати, це якщо: new.the_id якось перетвориться на NULL у триггері, який знаходиться на таблиці X.
Philᵀᴹ

@Phil: це, безумовно, причина проблеми. Ви повинні зробити це відповіддю.
Рене Ніффенеггер

@ RenéNyffenegger - проблема також виникає у програмах, які вставляють у таблиці без тригерів. Схоже, помилка з рівними можливостями.
Корбін березня

Відповіді:


4

Я впевнений, що це в кінцевому підсумку стане артефактом вашого коду чи драйвером .net, який ви використовуєте. Я створив для вас швидку демонстрацію за допомогою чистого SQL - PL / SQL і ніколи не отримую втрачене значення послідовності. Між іншим, курсор ref, який ви використовуєте, ймовірно, не потрібний і, ймовірно, впливає на продуктивність і читабельність коду - моя демонстрація включає в себе процедуру insert_record2, яка стабільно виконує на 10% швидше - приблизно на 26 секунд на моєму ноутбуці проти 36 для версії курсору ref. Я, принаймні, теж думаю, що це легше зрозуміти. Ви, очевидно, можете запустити модифіковану версію проти тестової бази даних разом із тригером аудиту.

/* 
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
До речі, версія з традиційним підходом використання тригера для стовпця the_id і наступної процедури також швидше створити або замінити ПРОЦЕДУРА Insert_Record3 (p_name IN dbse13142.name% type, p_userid IN dbse13142.userid% type, p_theid OUT dbse13142 .the_id% type) ПОЧАТОК ВСТАВЛЯЄТЬСЯ dbse13142 (ім'я, userid) VALUES (p_name, p_userid), повертаючи theid у p_theid; КІНЧ; /
Найл Літчфілд

Погодилися, що це, мабуть, проблема з кодом програми або драйвером. Мені просто цікаво, що може спричинити нульовий nextval як побічний ефект. Загадкові. Дякуємо за пораду щодо ефективності. Це хороша порада, яку я запропоную команді.
Корбін березня

1
Корбін, що я маю на увазі (і Кевін) - це те, що між вашим кодом та оракулом відбувається щось дивне - якщо ви запускаєте тест виключно в SQL, ви не отримаєте ефекту. Але дивіться коментар Філа щодо тригера аудиту (який ви можете спробувати відключити).
Niall Litchfield

Я розумію зроблені моменти. Проблема існує в програмах, що вставляються в таблиці з тригерами і без них, тому тригер не потрібен. Коли тригер існує, він просто вставляється в таблицю аудиту. Я підтвердив :new.the_id, що недоторканий. Я розумію, моє запитання - це довгостріл. Він стійкий до мого google-fu і тут декілька людей дряпають голову. Я просто зрозумів, що хтось може розпізнати симптом (і лікування), отримавши достатню кількість очних яблук. Дякуємо, що подивились.
Корбін березня

2

Спробуйте зробити тестовий випадок. Складіть макетну таблицю і вставте 100 000 записів, використовуючи свою послідовність із бази даних. Б'юсь об заклад, у вас не буде проблем. Далі спробуйте вставити те саме з вашої програми.

Чи можуть це бути викликані іншими проблемами, такими як невідповідність клієнта Oracle?

Ще одне рішення, яке вирішило б проблему, але не було проблемою - додати тригер на стіл.
Перед вставкою в таблицю на Dallas.X ЯК: the_id є нульовим ТАКІ ВИБІР x_seq.nextval INTO: the_id ВІД подвійний; КІНЦЯ ЯКЩО;


Я не можу відтворити проблему локально. Це трапляється лише на виробництві і там тільки нечасто. Моя думка полягає в тому, що ви неправі щодо клієнта Oracle. Проблема з’явилася пару тижнів тому під час випуску, де клієнт не оновлювався. Однак, здається, що щось не вживається між додатком та DB. Взаємодія інших споживачів, здається, працює добре. Нульова перевірка не є поганою ідеєю, але в ідеалі я хотів би отримати корінь проблеми проти роботи над нею. Хто знає, хоча? Обхід краще, ніж розбитий.
Корбін березня

0

У мене поки що немає коментарів, щоб коментувати коментарі, тому записуйте це як відповідь: Оскільки ви використовуєте версію Oracle> = 11.1, яка дозволяє послідовності в виразах PL / SQL замість SQL, спробуйте це:

   v_id := x_seq.nextval;

Замість цього:

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

Або, хоча я почув сумніви / підводні камені під час використання ".currval", можливо, опустіть окреме призначення v_id і використовуйте лише цей код ?:

 -- 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;

Вибачте, зараз не маю під рукою екземпляр 11 г, щоб спробувати це.


це однозначно не має значення. Я вживаю select into...в 11 стільки, скільки в 9i і 10г. Єдина користь від 11+ - це можливість явно посилатися на неї, як ви вказали.
Бен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.