19

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

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

MSDN 说 RowVersion

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

  • 每个数据库都有一个计数器,对于在数据库中包含 rowversion 列的表执行的每个插入或更新操作,该计数器都会递增。此计数器是数据库行版本。这跟踪数据库中的相对时间,而不是可以与时钟关联的实际时间。一个表只能有一个 rowversion 列。每次修改或插入具有 rowversion 列的行时,都会在 rowversion 列中插入递增的数据库 rowversion 值。

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

  • 当多个用户同时更新行时,您可以将 rowversion 列添加到表中以帮助维护数据库的完整性。您可能还想知道在不重新查询表的情况下更新了多少行和哪些行。

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

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

4

3 回答 3

17

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

系统更改号 (SCN) 是 Oracle 数据库使用的逻辑内部时间戳。SCN 对数据库中发生的事件进行排序,这是满足事务的 ACID 属性所必需的。Oracle 数据库使用 SCN 来标记 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                               
于 2013-12-10T07:34:14.697 回答
9

简单的答案是否定的 - 但很容易自己创建一个带有 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 等。

于 2013-12-10T08:14:24.520 回答
1

根据 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

于 2016-10-11T10:13:06.627 回答