检查表数据是否已更改?

时间:2013-11-17 04:35:18

标签: sql

我从几个表中提取数据,然后将数据传递给长时间运行的进程。我希望能够记录该进程使用的数据,然后查询数据库以检查自上次运行该进程以来是否有任何表已更改。

是否有解决此问题的方法应该适用于所有sql数据库?

我想到的一个可能的解决方案是使用一个单独的表,该表仅用于跟踪自进程运行以来数据是否已更改。该表包含一个“陈旧”标志。当我开始运行该过程时,stale设置为false。如果在操作所依赖的任何表中发生任何创建,更新或删除,我将陈旧设置为true。这是有效的解决方案吗?有更好的解决方案吗?

我的解决方案的一个问题是这样的情况:

一个用户开始在其中一个表中插入新行。 Stale设置为true,但新行尚未实际添加。另一个用户同时启动了长时间运行的进程,从表中提取数据并将标志设置为false。最后添加了该行。现在用于该过程的数据已过期,但该标志表明它不是陈旧的。交易能否解决这个问题?

修改

这是我的想法的一些SQL。不确定它是否有效,但只是为了让你更好地了解我的想法:

# First transaction reads the data and sets the flag to false
BEGIN TRANSACTION
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
  UPDATE flag SET stale = false
  SELECT * FROM DATATABLE  
COMMIT TRANSACTION

# Second transaction updates the data and sets the flag to true
BEGIN TRANSACTION
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
  UPDATE data SET val = 15 WHERE ID = 10
  UPDATE flag SET stale = true
COMMIT TRANSACTION    

我对交易或手写xml没有太多经验,所以可能存在问题。据我所知,两个可序列化的事务不能交错。如果我错了,请纠正我。

有没有办法只用第一笔交易来完成这个?该过程很少运行,但数据表的更新将更频繁地发生,因此在执行更新时不要锁定数据表会很好。

此外,SET TRANSACTION ISOLATION语法是否特定于MS?

1 个答案:

答案 0 :(得分:2)

陈旧标志可能会起作用,但时间戳会更好,因为它提供了更多关于记录年龄的元数据,可用于调整查询,例如,只提取超过5分钟的数据。

要解决在运行查询的同时插入行的问题,具有适当隔离级别的事务将有所帮助。对于行插入,更新和选择,至少使用具有隔离级别的事务来防止脏读取,以便在提交事务之前没有其他连接可以看到更新的数据。

如果您非常关注在记录拉动的同时更新发生的情况,您可以使用REPEATABLE READ或甚至SERIALIZABLE隔离级别,但这会降低DB访问速度。

您的SQLServer示例应该有效。对于备用数据库,这是一个适用于PostGres的示例:

交易1

BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- run queries that update the tables, then set last_updated column
UPDATE sometable SET last_updatee = now() WHERE id = 1;;
COMMIT;

交易2

BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- select data from tables, then set last_queried column
UPDATE sometable SET last_queried = now() WHERE id = 1;
COMMIT;

如果事务1启动,然后事务2在事务1完成之前启动,事务2将在更新期间阻塞,然后在事务1提交时将抛出错误。如果事务2首先启动,并且事务1在完成之前启动,则事务1将发生错误。您的应用程序代码或进程应该能够处理这些错误。

其他数据库使用类似的语法 - MySQL(使用InnoDB插件)要求您在开始事务之前设置隔离级别。