为多个组选择每组记录中的最新和特定版本

ora*_*nge 24 sql t-sql sql-server

问题:
我有一个记录数据行的表foo.每次更新行时,都会插入一个新行以及修订号.该表看起来像:

id  rev field
1   1   test1
2   1   fsdfs
3   1   jfds
1   2   test2
Run Code Online (Sandbox Code Playgroud)

请注意,在表中,最后一条记录是第一行的较新版本.

有没有人知道查询最新版本的行的有效方法,以及特定版本的记录?例如,对于一个查询rev=2将返回图2,3和第4行(未更换的第一行虽然)而用于查询rev=1的产率的那些行与转<= 1,在重复的ID的情况下,一个具有较高版本号是选择(记录:1,2,3).

我真的不确定这在SQL Server中是否可行...

我不想以迭代的方式返回结果.

Tim*_*Tim 40

要获得最新修订:

SELECT * from t t1
WHERE t1.rev = 
  (SELECT max(rev) FROM t t2 WHERE t2.id = t1.id)
Run Code Online (Sandbox Code Playgroud)

要获得特定的修订版本,在这种情况下为1(如果项目没有修订版本,则为下一个最小修订版本):

SELECT * from foo t1
WHERE t1.rev = 
  (SELECT max(rev) 
   FROM foo t2 
   WHERE t2.id = t1.id
   AND t2.rev <= 1)
Run Code Online (Sandbox Code Playgroud)

它可能不是最有效的方法,但是现在我无法想出更好的方法来做到这一点.

  • @Tim - 不对.SQL声明不是必要的.在这种情况下,SQL Server知道这种模式,计划非常简单.[计划图片](http://i.stack.imgur.com/MUb0m.jpg) (3认同)

Tre*_*reb 6

如果您想要每个字段的所有最新修订,您可以使用

SELECT C.rev, C.fields FROM (
  SELECT MAX(A.rev) AS rev, A.id
  FROM yourtable A
  GROUP BY A.id) 
AS B
INNER JOIN yourtable C
ON B.id = C.id AND B.rev = C.rev
Run Code Online (Sandbox Code Playgroud)

在你的例子中,会返回

 rev field
 1   fsdfs   
 1   jfds   
 2   test2
Run Code Online (Sandbox Code Playgroud)


Aak*_*shM 6

我就是这样做的.ROW_NUMBER()需要SQL Server 2005或更高版本

样本数据:

DECLARE @foo TABLE (
    id int,
    rev int,
    field nvarchar(10)
)

INSERT @foo VALUES
    ( 1, 1, 'test1' ),
    ( 2, 1, 'fdsfs' ),
    ( 3, 1, 'jfds' ),
    ( 1, 2, 'test2' )
Run Code Online (Sandbox Code Playgroud)

查询:

DECLARE @desiredRev int

SET @desiredRev = 2

SELECT * FROM (
SELECT 
    id,
    rev,
    field,
    ROW_NUMBER() OVER (PARTITION BY id ORDER BY rev DESC) rn
FROM @foo WHERE rev <= @desiredRev 
) numbered
WHERE rn = 1
Run Code Online (Sandbox Code Playgroud)

内部SELECT返回所有相关记录,并在每个id组(即那个PARTITION BY)内,按降序排序时计算行号rev.

外部SELECT只选择rev每个id组中的第一个成员(因此,最高成员).

输出时间@desiredRev = 2:

id          rev         field      rn
----------- ----------- ---------- --------------------
1           2           test2      1
2           1           fdsfs      1
3           1           jfds       1
Run Code Online (Sandbox Code Playgroud)

输出时间@desiredRev = 1:

id          rev         field      rn
----------- ----------- ---------- --------------------
1           1           test1      1
2           1           fdsfs      1
3           1           jfds       1
Run Code Online (Sandbox Code Playgroud)


Ton*_*gan 5

这是一个替代解决方案,会产生更新成本,但由于避免了计算,因此读取最新数据行的效率更高MAX(rev)。当您对表的子集进行批量更新时,它也适用。我需要这种模式,以确保可以有效地切换到通过长时间运行的批处理更新而更新的新数据集,而没有任何时间窗口可以看到部分更新的数据。

老化

  • rev列替换age
  • 使用过滤器创建当前最新数据的视图: age = 0
  • 要创建数据的新版本...
    • INSERT:用age = -1-的新行-这是我运行缓慢的长期批处理过程。
    • 更新:UPDATE table-name SET age = age + 1对于子集中的所有行。这会将视图切换到新的最新数据(年龄= 0),并且还会在单个事务中老化较旧的数据。
    • 删除:age > N子集中有行-可以选择清除旧数据

索引编制

  • 创建一个复合索引,age然后使用,id这样视图将变得美观,快速,也可以用于按ID查找。尽管此键实际上是唯一的,但在对行进行老化(在期间UPDATE SET age=age+1)时,它暂时是不唯一的,因此您需要使它成为不唯一的,理想情况下是使其成为聚集索引。如果你需要找到一个给定的所有版本id下令age,你可能需要一个额外的非唯一索引id,然后age

回滚

最后...可以说您的日子不好过,并且批处理过程中断了。您可以通过运行以下命令快速恢复到先前的数据集版本:

  • UPDATE table-name SET age = age - 1 -回滚版本
  • DELETE table-name WHERE age < 0 -清理坏东西

注意:我建议命名老化列,RowAge而不是age指示使用此模式,因为很明显,它是与数据库相关的值,并且可以补充SQL Server的RowVersion命名约定。它也不会与需要返回一个人的年龄的列或视图冲突。

与其他解决方案不同,此模式适用于非SQL Server数据库。

  • @ReiMiyasaka任何多版本解决方案都不能在同一个表中使用自动增量,所以是的,您要么使用单独的表,要么计算 NEW_ID = MAX(ID) +1 并在由于并发而失败时重试 INSERT INSERT 尝试返回相同的 NEW_ID。您可能希望表上有一个对于行和自动增量唯一的附加 ID 列。 (2认同)