使用LINQ to SQL从时间戳列查询

7

我的表有一个名为"RowVer"的时间戳列,LINQ将其映射到类型System.Data.Linq.Binary。对我来说,这种数据类型似乎是无用的,因为(除非我漏了什么),我无法像这样做:

// Select all records that changed since the last time we inserted/updated.
IEnumerable<UserSession> rows = db.UserSessions.Where
( usr => usr.RowVer > ???? );

因此,我正在考虑的解决方案之一是添加一个名为RowTrack的新“计算列”,该列在SQL中定义如下:
CREATE TABLE UserSession
(
RowVer timestamp NOT NULL,
RowTrack  AS (convert(bigint,[RowVer])),
-- ... other columns ...
)

这让我可以按照自己的意愿查询数据库:
// Select all records that changed since the last time we inserted/updated.
IEnumerable<UserSession> rows = db.UserSessions.Where
( usr => usr.RowTrack > 123456 );

这是一种不好的做法吗?在查询计算列时性能如何?有更好的解决方法吗?

另外,我正在开发针对Sql Server 2000的应用程序以实现最大限度的向后兼容性,但我可以说服老板将2005作为最低公共分母。

4个回答

9
根据Diego Frata这篇文章中的介绍,有一个技巧可以使时间戳能够从LINQ中进行查询。
这个技巧是定义一个接受两个System.Data.Linq.Binary参数的Compare方法。
public static class BinaryComparer
{
 public static int Compare(this Binary b1, Binary b2)
 {
 throw new NotImplementedException();
 }
}

注意,函数不需要实现,只有它的名称(Compare)很重要。

查询将类似于以下内容:

Binary lastTimestamp = GetTimeStamp();
var result = from job in c.GetTable<tblJobs>
             where BinaryComparer.Compare(job.TimeStamp, lastTimestamp)>0
             select job;

(这是在工作时间戳大于上次时间戳时使用)

编辑: 如果您需要在SQL之外的环境中使用该方法,请参见Rory MacLeod的答案


在Entity Framework(4.3.1)中也适用,使用Byte[]代替Binary作为比较器。 - John Landheer
迪亚哥的个人资料和博客链接已经失效。 - Stephen Turner

6

SQL Server的“timestamp”只是一个指示符,表示记录已更改,它实际上并不代表日期/时间。(虽然它应该在每次修改数据库中的记录时递增)

请注意,它会回到零(尽管不经常),因此唯一安全的测试是检查该值是否已更改,而不是它是否大于某些任意先前的值。

您可以将TimeStamp列值传递给Web表单,然后当提交表单时,查看表单中的TimeStamp是否与当前记录中的值不同——如果它们不同,则表示其他人在期间更改并保存了该记录。


如果您只需要测试值是否已更改,则可以使用“a == b”比较二进制实例。 - Lucas
谢谢您的帖子。现在我更清楚为什么不应该尝试重新使用我的RowVer列。 - Wayne Bloss
1
实际上,时间戳列非常适合这个目的。除非您对单个表进行了超过2^64次修改,否则您不必担心它会溢出。作为参考,以每秒1,000,000次写入计算,这将需要近600,000年的时间。 - Josh
1
@Josh 时间戳/行版本是针对整个数据库而不仅仅是表格。 - Stephen Turner

4
// 选择自上次插入/更新以来更改的所有记录。 有没有更好的解决方法?
为什么不使用两列,一列用于创建日期,另一列用于最后修改日期。我认为这是处理此场景的更传统方式。

谢谢您的帖子。它让我更深入地思考这个问题,意识到日期时间更好,因为它代表了更有用的信息。现在我可以检测记录过期的时间,而不仅仅是是否过期。 - Wayne Bloss
不要忘记为它们添加索引 - Simon_Weaver
日期/时间字段的问题在于使用它们进行比较时会出现来自不同客户端或在数据库之间同步数据时可能产生的时钟差异,因为没有单一的时间来源。一个简单的时间戳(rowversion)通常是检测更改的简单有效方法。 - mhapps

2

继承自jaraics的答案,你也可以为Compare方法提供一个实现,使其能够在查询之外使用:

public static class BinaryExtensions
{
    public static int Compare(this Binary b1, Binary b2)
    {
        if (b1 == null)
            return b2 == null ? 0 : -1;

        if (b2 == null)
            return 1;

        byte[] bytes1 = b1.ToArray();
        byte[] bytes2 = b2.ToArray();
        int len = Math.Min(bytes1.Length, bytes2.Length);
        int result = memcmp(bytes1, bytes2, len);

        if (result == 0 && bytes1.Length != bytes2.Length)
        {
            return bytes1.Length > bytes2.Length ? 1 : -1;
        }

        return result;
    }

    [DllImport("msvcrt.dll")]
    private static extern int memcmp(byte[] arr1, byte[] arr2, int cnt);
}

使用memcmp函数是从这个回答中得到的,该回答解决了比较字节数组的问题。如果两个数组长度不同,但较长的数组以与较短数组相同的字节开头,则认为较长的数组大于较短的数组,即使多出来的字节都是零。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接