这在 Firebird 中并不像在 MySQL 中那样简单。如果事先知道database_id 的数量,您可以为每个id 分配一个序列并在触发器中使用它,但是对于大量的id,这很快就会变得笨拙。
我的其余答案假设使用 Firebird 2.5(我已经使用 Firebird 2.5.2 Update 1 对其进行了测试)。
如果我们只有database_ids 1 和 2,我们可以创建两个序列:
CREATE SEQUENCE multisequence_1;
CREATE SEQUENCE multisequence_1;
当使用没有序列的 id 时,我们需要一个例外:
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 语句。这可以通过使用EXECUTE STATEMENT 和为下一个序列值动态生成的查询来简化。如果您无法提前控制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 可能允许您使用该序列,但它也可能导致虚假错误,如锁冲突。另请注意,不鼓励在 EXECUTE STATEMENT 中使用 DDL(请参阅 warning in the documentation)。
在生产环境中使用此解决方案之前,我强烈建议在负载下彻底测试此解决方案!
请注意,WITH AUTONOMOUS TRANSACTION 子句在技术上不是创建序列所必需的,但需要确保序列对其他事务也可见(并且如果原始事务回滚则不会被删除)。
还要注意单个 Firebird 数据库中的最大序列数(或:生成器):+/- 32758,请参阅 Firebird Generator Guide: How many generators are available in one database?。