SQL Server:Oracle中的RowVersion等效版本



Oracle的数据类型是否与SQL Server的RowVersion相似?

插入或更新行时,相应的Version列(类型为RowVersion)会自动更新。

MSDN介绍RowVersion:

  • 是一种公开自动生成的唯一二进制文件的数据类型数据库中的数字。rowversion通常用作一种机制用于版本冲压表行。存储大小为8字节。这个rowversion数据类型只是一个递增的数字,而不是保留日期或时间。

  • 每个数据库都有一个计数器,每次插入或对包含数据库中的rowversion列。此计数器是数据库rowversion。这跟踪数据库中的相对时间,而不是可以与时钟相关联的实际时间。一张桌子只能有一个rowversion列。每次具有rowversion列的行被修改或插入时,递增的数据库rowversion值为插入rowversion列中。

  • 您可以使用行的rowversion列来轻松确定自上次读取该行以来,该行中的任何值都已更改。如果对行进行任何更改时,rowversion值都会更新。如果没有对行进行更改时,rowversion值与之前已读取。

  • 您可以将rowversion列添加到表中,以帮助维护当多个用户在同时。您可能还想知道有多少行以及哪些行更新而不重新查询表。

我们正在使用oracle设计一个数据模型,并希望使用Version列来管理并发。

我也想知道在甲骨文的世界里是否还有更好的方法。

Oracle具有SCN(系统更改编号):http://docs.oracle.com/cd/E11882_01/server.112/e10713/transact.htm#CNCPT039

系统更改编号(SCN)是Oracle数据库使用的逻辑内部时间戳。SCN对数据库中发生的事件进行排序,这对于满足事务的ACID属性是必要的。Oracle数据库使用SCN来标记SCN,在此之前,所有更改都已知在磁盘上,这样恢复就可以避免应用不必要的重做。数据库还使用SCN来标记一组数据不存在重做的点,以便可以停止恢复。

SCN以单调递增的顺序出现。Oracle数据库可以像时钟一样使用SCN,因为观测到的SCN表示逻辑时间点,重复观测会返回相等或更大的值。如果一个事件的SCN低于另一个事件,则它发生在数据库的较早时间。多个事件可能共享同一SCN,这意味着它们相对于数据库同时发生。

每个事务都有一个SCN。例如,如果事务更新了一行,则数据库会记录发生此更新的SCN。此事务中的其他修改具有相同的SCN。当事务提交时,数据库会记录此提交的SCN。


使用ORA_ROWSCN伪列检查行的当前SCN:
http://docs.oracle.com/cd/B28359_01/server.111/b28286/pseudocolumns007.htm#SQLRF51145

一个例子:

SELECT ora_rowscn, t.* From test t;

演示-->http://www.sqlfiddle.com/#!4/535bc/1
(在SQLFiddle上,显式提交显然不起作用——在真实的数据库上,每次提交都会增加SCN)。


一个"真实"数据库的例子:

CREATE TABLE test(
  id int,
  value int
);
INSERT INTO test VALUES(1,0);
COMMIT;
SELECT ora_rowscn, t.* FROM test t;
ORA_ROWSCN         ID      VALUE
---------- ---------- ----------
   3160728          1          0
UPDATE test SET value = value + 1 WHERE id = 1;
COMMIT;
SELECT ora_rowscn, t.* FROM test t;
ORA_ROWSCN         ID      VALUE
---------- ---------- ----------
   3161657          1          1
UPDATE test SET value = value + 1 WHERE id = 1;
COMMIT;
SELECT ora_rowscn, t.* FROM test t;
ORA_ROWSCN         ID      VALUE
---------- ---------- ----------
   3161695          1          2 

如果知道事务的SCN,我们可以使用倒叙查询来获得该行的过去值:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28424/adfns_flashback.htm#g1026131

一个例子:

SELECT t.*,
       versions_startscn, versions_starttime,
       versions_endscn, versions_endtime,
       versions_xid, versions_operation
FROM test VERSIONS BETWEEN SCN MINVALUE AND MAXVALUE t;
        ID      VALUE VERSIONS_STARTSCN VERSIONS_STARTTIME  VERSIONS_ENDSCN VERSIONS_ENDTIME    VERSIONS_XID     VERSIONS_OPERATION
---------- ---------- ----------------- ------------------- --------------- ------------------- ---------------- ------------------
         1          2           3161695 13/12/10 08:19:39                                       06000300EA070000 U                  
         1          1           3161657 13/12/10 08:18:39           3161695 13/12/10 08:19:39   06001200EA070000 U                  
         1          0                                               3161657 13/12/10 08:18:39                         

SELECT t.*,
       versions_startscn, versions_starttime,
       versions_endscn, versions_endtime,
       versions_xid, versions_operation
FROM test VERSIONS BETWEEN SCN 3161657 AND 3161657 t;
        ID      VALUE VERSIONS_STARTSCN VERSIONS_STARTTIME  VERSIONS_ENDSCN VERSIONS_ENDTIME    VERSIONS_XID     VERSIONS_OPERATION
---------- ---------- ----------------- ------------------- --------------- ------------------- ---------------- ------------------
         1          1           3161657 13/12/10 08:18:39                                       06001200EA070000 U                               

简单的答案是否定的,但自己创建一个NUMBER列和一个触发器来设置/更新它很容易。

Oracle 11gR2:的一个简单示例

CREATE SEQUENCE global_rowversion_seq;
ALTER TABLE mytable1 ADD rowversion NUMBER;
ALTER TABLE mytable2 ADD rowversion NUMBER;
CREATE TRIGGER mytable1_biu
   BEFORE INSERT OR UPDATE
   ON mytable1
   FOR EACH ROW
BEGIN
  :NEW.rowversion := global_rowversion_seq.NEXTVAL;
END mytable1_biu;
CREATE TRIGGER mytable2_biu
  BEFORE INSERT OR UPDATE
  ON mytable2
  FOR EACH ROW
BEGIN
  :NEW.rowversion := global_rowversion_seq.NEXTVAL;
END mytable2_biu;

(如果您使用的是早期的Oracle版本,则触发器中的分配必须使用查询完成,例如:

  SELECT global_rowversion_seq.NEXTVAL
  INTO :NEW.rowversion
  FROM dual;

现在,请记住,在某些情况下,这种设计可能会在极端情况下(例如,具有极高插入/更新活动的数据库)对性能产生影响,因为使用相同序列的所有数据库插入/更新都会发生争用。当然,在这种情况下,你可能会从一开始就避免触发。

根据rowversion列的使用方式,最好为每个表使用单独的序列。当然,这意味着rowversion将不再是全局唯一的,但如果您只对比较表中行的更改感兴趣,那么这也没关系。

另一种方法是单独提前每行的计数器-这不需要序列,并且允许您检测对一行的更改(但不允许将任何行与另一行进行比较):

ALTER TABLE mytable ADD rowversion NUMBER;
CREATE TRIGGER mytable_biu
  BEFORE INSERT OR UPDATE
  ON mytable
  FOR EACH ROW
BEGIN
  :NEW.rowversion := NVL(:OLD.rowversion, 0) + 1;
END mytable_biu;

每一行都将插入rowversion=1,然后对该行的后续更新将使其增加到2、3等。

根据oracle文档,您可以使用ORA_ROWSCN,并使用"ROWDEPENDENCIES"进行oracle行级依赖性跟踪。而不是每个物理数据块。

参考编号:https://docs.oracle.com/cd/B19306_01/server.102/b14200/pseudocolumns007.htm

http://www.dba-oracle.com/t_row_scn_rowdependencies.htm

最新更新