This is not so simple to do in Firebird as it is in MySQL. If the number of database_id
is known in advance, you can allocate a sequence for each id and use that in a trigger, but this quickly becomes unwieldy for a large number of ids.
The rest of my answer assumes the use of Firebird 2.5 (I have tested it with Firebird 2.5.2 Update 1).
If we only have database_id
s 1 and 2, we can create two sequences:
CREATE SEQUENCE multisequence_1;
CREATE SEQUENCE multisequence_1;
We need an exception when an id is used that has no sequence:
CREATE OR ALTER EXCEPTION no_sequence 'No corresponding sequence found';
We can then use the following trigger:
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
As you can see this will quickly lead to a lot of IF/ELSE statements. This can probably be simplified by using EXECUTE STATEMENT
and a dynamically generated query for the next sequence value. This won't work if you cannot control the number of database_id
values (and their sequences) in advance.
You can try to solve this using dynamic queries as shown below. This might have its own problems (especially if there is a high volume of inserts) because EXECUTE STATEMENT
has some overhead, and it might also lead to problems because of the use of dynamic DDL (eg lock/update-conflicts on the metadata tables).
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
This code is still susceptible to multiple transaction trying to create the same sequence. Adding a WHEN ANY DO
after the statement that (attempted to) create the sequence, might allow you to use the sequence anyway, but it might also lead to spurious errors like lock conflicts. Also note that using DDL in EXECUTE STATEMENT
is discouraged (see the warning in the documentation).
Before using this solution in a production situation, I'd strongly suggest to thoroughly test this under load!
Note that the WITH AUTONOMOUS TRANSACTION
clause is technically not necessary for creating the sequence, but it is required to ensure the sequence is also visible to other transactions (and doesn't get deleted if the original transaction is rolled back).
Also be aware of the maximum number of sequences (or: generators) in a single Firebird database: +/- 32758, see Firebird Generator Guide: How many generators are available in one database?.