我的问题有点棘手,因为这主要是一个逻辑问题。
我试图通过将所有内容读取到内存中(但仅读取那些记录)来优化我的应用程序速度,这些记录自“ last read” =上次加载记录的最大时间戳以来就发生了变化。
FirebirdSQL数据库引擎不允许直接在“触发后”中更新字段,因此显然使用“更新或插入之前”触发器来更新字段new.last_changed = current_timestamp;
事实证明,这是一种完全错误的方法,因为这些触发器会在交易开始时触发!
因此,如果有一个事务比另一个事务花费更多的时间,则保存的“最后更改时间”将比两次之间触发并完成的短暂突发事务要短。
1. tr.: 13:00:01.400 .............................Commit
<<该记录将被跳过!
2. tr.: 13:00.01.500......Commit
<<这里将进行数据读取。
下一次读取将是>= 13:00.01.500
重写所有触发器,因此它们会在之后触发并调用UPDATE orders SET ...
<< ,但这会导致循环的,自调用的无尽事件。
SET_CONTEXT
lock是否会干扰多行更新和嵌套触发器?
(如果在同一个事务中运行多个更新,我认为这种方法很好不太可能。)
所有这些通用解决方案是什么?
我想做的是仅读取 那些自上次读取以来实际上已更改的数据库记录。为此,我需要引擎在提交后更新记录。 (不是在“中间”。)
此触发器不好,因为它将在更改时触发(而不是在提交后触发):
alter trigger SYNC_ORDERS active after insert or update position 999 AS
declare variable N timestamp;
begin
N = cast('NOW' as timestamp);
if (new.last_changed <> :N) then
update ORDERS set last_changed= :N where ID=new.ID;
end
然后从应用程序中执行:
Query1.SQL.Text := 'SELECT * FROM orders WHERE last_changed >= ' + DateTimeToStr( latest_record );
Query1.Open;
latest_record := Query1.FieldByName('last_changed').asDateTime;
..此代码将仅列出第二个事务中提交的记录(较早),而不列出第一个运行时间更长的事务(稍后提交)。
似乎我和here...有相同的问题,但特别是对于FirebirdSQL。
那里确实没有任何好的解决方案,但是给了我一个主意:
-如果我创建一个额外的表并在每个表的5分钟之前记录更改,该怎么办?
-在执行每个SQL查询之前,首先,我会要求该表中的所有更改(通过ID增长排序)!
-删除超过23小时的行
ID TableID Changed
===========================
1 5 2019.11.27 19:36:21
2 5 2019.11.27 19:31:19
正如Arioch已经建议的那样,一种解决方案是:
BEFORE INSERT OR UPDATE
上创建一个“记录器表”
由每个表触发ON TRANSACTION COMMIT
触发器但是,不会...
last_sequence INT64 DEFAULT NULL
列LAST_GEN
ON TRANSACTION COMMIT
触发器内使用gen_id(LAST_GEN,1)更新每个表的每个NULL行BEFORE INSERT OR UPDATE
触发器上再次将其设置为NULL 因此,基本上将记录的last_sequence
列切换为:
NULL > 1 > NULL > 34
...每次对其进行修改。
这样我:
WHERE last_sequence>1;
查询表。 我只是害怕:如果ON TRANSACTION COMMIT
触发器尝试更新last_sequence
字段,而第二个事务的ON之前触发器将锁定记录,会发生什么情况(另一张桌子的)?
这会发生吗?
答案 0 :(得分:0)
最终的解决方案基于以下想法:
BEFORE INSERT OR UPDATE
触发器可以推迟事务处理时间:RDB$SET_CONTEXT('USER_TRANSACTION', 'table31', current_timestamp
); ON TRANSACTION COMMIT
触发器收到这样的上下文,则可以将其+时间插入“日志表”中。 1。)
create trigger ORDERS_BI active before insert or update position 0 AS
BEGIN
IF (NEW.ID IS NULL) THEN
NEW.ID = GEN_ID(GEN_ORDERS,1);
RDB$SET_CONTEXT('USER_TRANSACTION', 'orders_table', current_timestamp);
END
2,3。)
create trigger TRG_SYNC_AFTER_COMMIT ACTIVE ON transaction commit POSITION 1 as
declare variable N TIMESTAMP;
declare variable T VARCHAR(255);
begin
N = cast('NOW' as timestamp);
T = RDB$GET_CONTEXT('USER_TRANSACTION', 'orders_table');
if (:T is not null) then begin
if (:N < :T) then T = :N; --system time changed eg.: daylight saving" -1 hour
if (datediff(second from :T to :N) > 60 ) then --more than 1min. passed
insert into "SYNC_PAST_TIMES" (ID, TABLE_NUMBER, TRG_START, SYNC_TIME, C_USER)
values (GEN_ID(GEN_SYNC_PAST_TIMES, 1), 31, cast(:T as timestamp), :N, CURRENT_USER);
end;
T = RDB$GET_CONTEXT('USER_TRANSACTION', 'details_table');
-- other tables ...
when any do EXIT;
end