2017-02-20 58 views
0

У меня есть хранимая процедура вставки записи в таблицу в нашем хранилище данных Teradata со следующим утверждением:Capture Ошибка вставки в Teradata

INSERT INTO UAT_AUDIT_VIEWS.AUDIT_BATCH(
BATCH_KEY 
,AUDIT_STATUS_KEY 
,BATCH_START_DATETIME 
,BATCH_END_DATETIME 
,BATCH_OWNER 
,BATCH_EXECUTION_START_DATETIME 
,BATCH_EXECUTION_END_DATETIME 
) 
VALUES(
(SELECT COALESCE(MAX(BATCH_KEY),0)+1 FROM UAT_AUDIT_VIEWS.AUDIT_BATCH) 
,5 --PENDING 
,'1900-01-01 00:00:00' 
,'2999-12-31 00:00:00' 
,:P_BATCH_OWNER 
,CURRENT_TIMESTAMP 
,'2999-12-31 00:00:00' 
); 

Обратите внимание, что у меня есть единственность ограничение на первичный ключ BATCH_KEY. В некоторых случаях моя инструкция insert не выполнялась, поскольку первичный ключ уже существует в таблице. Когда это произойдет, я хочу, чтобы моя хранимая процедура зациклилась и повторила попытку вставки до тех пор, пока она не будет успешной.

Я попробовал несколько решения с использованием следующим, но безуспешно:

  • DECLARE CONTINUE HANDLER FOR SQLEXCEPTION, чтобы избежать хранимую процедуры потерпеть неудачу, когда вставка терпит неудачу
  • A WHILE DO петли, чтобы повторить попытку Вкладыша

Не могли бы вы описать, как бы вы справились с таким сценарием? Вот упрощенный вариант процедуры тестирования хранятся я построил, чтобы проверить это (который не работает):

REPLACE PROCEDURE DEV_AUDIT_NEW.ARO_TEST_INSERT() 
BEGIN 
     DECLARE V_BATCH_KEY_CREATED VARCHAR(100); 
     DECLARE V_COUNTER SMALLINT DEFAULT 1; 
     DECLARE CONTINUE HANDLER FOR SQLEXCEPTION 

     SET V_BATCH_KEY_CREATED = NULL; 
     WHILE V_BATCH_KEY_CREATED IS NULL 
     DO 
      INSERT INTO DEV_AUDIT_NEW.AUDIT_BATCH_TEST_LOG(LOG_DESC) VALUES(V_BATCH_KEY_CREATED); 

      INSERT INTO DEV_AUDIT_NEW.AUDIT_BATCH_TEST(BATCH_KEY,BATCH_OWNER) VALUES(V_COUNTER,'B'); 
      SELECT BATCH_KEY 
      INTO :V_BATCH_KEY_CREATED 
      FROM DEV_AUDIT_NEW.AUDIT_BATCH_TEST 
      WHERE BATCH_KEY=V_COUNTER AND BATCH_OWNER='B'; 
      SET V_COUNTER=V_COUNTER+1; 

      INSERT INTO DEV_AUDIT_NEW.AUDIT_BATCH_TEST_LOG(LOG_DESC) VALUES(V_BATCH_KEY_CREATED); 
     END WHILE; 
END; 
+0

Ключ может существовать только при запуске SP параллельно. Вместо того, чтобы решать проблемы с параллелизмом, вы должны просто переключиться на блокировку WRITE: 'LOCK TABLE UAT_AUDIT_VIEWS.AUDIT_BATCH ДЛЯ WRITE INSERT ...' – dnoeth

+0

У меня есть несколько команд, которые вызывают параллельную хранимую процедуру. Мы внедрили WRITE LOCK в качестве первого решения, но в конечном итоге это привело к возникновению проблем с блокировкой при одновременном доступе. Вот почему я хотел бы удалить его, захватить ошибку вставки и повторить попытку. Если у вас есть другие варианты, мне было бы интересно? –

+1

Вставка, которую вы опубликовали, не может создавать взаимоблокировки как есть при параллельном запуске с блокировкой WRITE на уровне таблицы. Другое решение (которое масштабируется лучше) основано на таблице последовательности с одной строкой для этой таблицы, сохраняющей максимальное значение, ваш SP читает текущее значение, увеличивает его на единицу и записывает обратно. – dnoeth

ответ

1

Это слишком долго, как комментарий.

Ваш текущий подход должен выполнить полное сканирование таблицы, чтобы получить MAX (по умолчанию используется блокировка чтения на уровне таблицы), но вставки по умолчанию для записи блокируются на уровне хэш-строки. Когда вы запрашиваете Write на уровне таблицы, он должен предотвращать взаимоблокировки.

Когда вы определяете какую-либо таблицу последовательности, каждый доступ представляет собой UPI-доступ, например.

CREATE SET TABLE Sequences 
(
    SequenceName VARCHAR(128) CHARACTER SET Unicode NOT CaseSpecific NOT NULL, 
    nextVal BIGINT NOT NULL DEFAULT 1 
) 
UNIQUE PRIMARY INDEX (SequenceName) 
; 

REPLACE PROCEDURE NextVal (IN SequenceName VARCHAR(128) CHARACTER SET Unicode, OUT NextVal BIGINT) 
BEGIN 
    BEGIN REQUEST 
     LOCK ROW WRITE 
     SELECT nextVal INTO :nextVal FROM Sequences 
     WHERE SequenceName = :SequenceName; 

     UPDATE Sequences SET nextVal = nextVal + 1 
     WHERE SequenceName = :SequenceName; 
    END REQUEST; 
END; 

Инициализировать новую последовательность:

INSERT INTO sequences ('mytable', 1); 

Получить следующее значение:

CALL nextVal('mytable', nextval);` 

Edit:

Вы можете легко проверить его с помощью BTEQ ведение журнала на несколько сеансов параллельно , например запустить 1000 вызовов через 10 сеансов:

.set session 10; 
.logon ...; 
select * from sequences where SequenceName = 'mytable'; 

.repeat 1000 
CALL nextVal('mytable', nextval); 

select * from sequences where SequenceName = 'mytable'; 

Смотреть значения возвращающихся последовательно, не тупики :-)

+0

Спасибо, я догадываюсь, что я достиг моих технических ограничений здесь ... Знаете ли вы, что произойдет, если два процесса одновременно вызовут SP NextVal? Предоставляет ли эта реализация гарантия, что он не вернет тот же nextVal для этих двух процессов, и ни один из них не столкнется с тупиком? Я дам ему попробовать ... –

+0

@ Alexis.Rolland: Отредактировал свой ответ, показал, как его проверить – dnoeth