В Firebird это не так просто сделать, как в MySQL. Если количество database_id
известно заранее, вы можете выделить последовательность для каждого идентификатора и использовать это в триггере, но это быстро становится громоздким для большого количества идентификаторов.
Остальная часть моего ответа предполагает использование Firebird 2.5 (я тестировал его с Firebird 2.5.2 Update 1).
Если у нас есть только database_id
s 1 и 2, мы можем создать две последовательности:
CREATE SEQUENCE multisequence_1;
CREATE SEQUENCE multisequence_1;
Нам нужно исключение, когда используется идентификатор, не имеющий последовательности:
CREATE OR ALTER EXCEPTION no_sequence 'No corresponding sequence found';
Затем мы можем использовать следующий триггер:
CREATE OR ALTER TRIGGER multisequence_BI FOR multisequence
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
IF (NEW.database_id = 1) THEN
NEW.table_id = NEXT VALUE FOR multisequence_1;
ELSE IF (NEW.database_id = 2) THEN
NEW.table_id = NEXT VALUE FOR multisequence_2;
ELSE
EXCEPTION no_sequence;
END
Как видите, это быстро приведет к множеству операторов IF / ELSE. Вероятно, это можно упростить, используя _ 6_ и динамически сгенерированный запрос для следующего значения последовательности. Это не сработает, если вы не можете заранее контролировать количество database_id
значений (и их последовательности).
Вы можете попытаться решить эту проблему с помощью динамических запросов, как показано ниже. Это может иметь свои собственные проблемы (особенно при большом объеме вставок), потому что EXECUTE STATEMENT
имеет некоторые накладные расходы, а также может привести к проблемам из-за использования динамического DDL (например, конфликты блокировок / обновлений в таблицах метаданных).
CREATE OR ALTER TRIGGER multisequence_BI FOR multisequence
ACTIVE BEFORE INSERT POSITION 0
AS
DECLARE new_id INTEGER;
DECLARE get_sequence VARCHAR(255);
BEGIN
get_sequence = 'SELECT NEXT VALUE FOR multisequence_' || NEW.database_id ||
' FROM RDB$DATABASE';
BEGIN
EXECUTE STATEMENT get_sequence INTO :new_id;
WHEN SQLCODE -104 DO
BEGIN
EXECUTE STATEMENT
'CREATE SEQUENCE multisequence_' || NEW.database_id
WITH AUTONOMOUS TRANSACTION;
EXECUTE STATEMENT get_sequence INTO :new_id;
END
END
NEW.table_id = new_id;
END
Этот код по-прежнему уязвим для нескольких транзакций, пытающихся создать одну и ту же последовательность. Добавление WHEN ANY DO
после оператора, который (пытался) создать последовательность, может позволить вам использовать последовательность в любом случае, но это также может привести к ложным ошибкам, таким как конфликты блокировок. Также обратите внимание, что использование DDL в EXECUTE STATEMENT
не рекомендуется (см. предупреждение в документации).
Прежде чем использовать это решение в производственной ситуации, я настоятельно рекомендую тщательно протестировать его под нагрузкой!
Обратите внимание, что предложение WITH AUTONOMOUS TRANSACTION
технически не требуется для создания последовательности, но необходимо, чтобы последовательность была также видима для других транзакций (и не удаляется при откате исходной транзакции).
Также помните о максимальном количестве последовательностей (или: генераторов) в одной базе данных Firebird: +/- 32758, см. Руководство по генераторам Firebird: сколько генераторов доступно в одной базе данных?.
person
Mark Rotteveel
schedule
08.05.2014