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

时间:2012-02-24 12:25:32

标签: sql sql-server tsql

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

id  rev field
1   1   test1
2   1   fsdfs
3   1   jfds
1   2   test2

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

有没有人知道查询最新版本行的有效方法,以及特定版本的记录?例如,rev=2的查询将返回第2,3和第4行(不是替换的第1行),而rev=1的查询产生rev< = 1的行,如果是重复的ID,选择具有较高版本号的ID(记录:1,2,3)。

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

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

7 个答案:

答案 0 :(得分:36)

仅获取最新修订:

SELECT * from t t1
WHERE t1.rev = 
  (SELECT max(rev) FROM t t2 WHERE t2.id = t1.id)

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

SELECT * from foo t1
WHERE t1.rev = 
  (SELECT max(rev) 
   FROM foo t2 
   WHERE t2.id = t1.id
   AND t2.rev <= 1)

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

答案 1 :(得分: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' )

查询:

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

内部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

@desiredRev = 1时的输出:

id          rev         field      rn
----------- ----------- ---------- --------------------
1           1           test1      1
2           1           fdsfs      1
3           1           jfds       1

答案 2 :(得分:4)

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

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

对于您的示例,将返回

 rev field
 1   fsdfs   
 1   jfds   
 2   test2

答案 3 :(得分:3)

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

老化

  • rev列替换为age
  • 使用过滤器创建当前最新数据的视图:age = 0
  • 要创建新版本的数据......
    • INSERT:age = -1的新行 - 这是我长时间运行的批处理过程。
    • UPDATE:UPDATE table-name SET age = age + 1表示子集中的所有行。这会将视图切换为新的最新数据(行= 0),并在单个事务中对旧数据进行老化。
    • DELETE:子集中包含age > N的行 - 可选择清除旧数据

索引

  • 使用age然后id创建一个复合索引,这样视图就会很好,也很快,也可以用来查找id。尽管此键实际上是唯一的,但是当您使行老化时(UPDATE SET age=age+1期间)它暂时不是唯一的,因此您需要使其非唯一且理想情况下是聚簇索引。如果您需要查找给定id的所有版本,则可能需要id上的其他索引。

最后......让我们说你有糟糕的一天,批处理中断了。您可以通过运行以下命令快速恢复到以前的数据集版本:

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

注意:我建议命名列名RowAge而不是age来表示正在使用此模式,因为它更清楚地表明它与数据库相关的值并且它补充了SQL Server的RowVersion命名惯例。它也不会与需要返回一个人年龄的专栏或视图发生冲突。

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

答案 4 :(得分:2)

SELECT
  MaxRevs.id,
  revision.field
FROM
  (SELECT
     id,
     MAX(rev) AS MaxRev
   FROM revision
   GROUP BY id
  ) MaxRevs
  INNER JOIN revision 
    ON MaxRevs.id = revision.id AND MaxRevs.MaxRev = revision.rev

答案 5 :(得分:1)

SELECT foo.* from foo 
left join foo as later 
on foo.id=later.id and later.rev>foo.rev 
where later.id is null;

答案 6 :(得分:0)

这个怎么样?

select id, max(rev), field from foo group by id

查询特定版本,例如修订版1,

select id, max(rev), field from foo where rev <= 1 group by id