【问题标题】:Querying a timestamp column from LINQ to SQL从 LINQ to SQL 查询时间戳列
【发布时间】:2009-02-13 21:08:25
【问题描述】:

我的表有一个名为“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 成为最低公分母。

【问题讨论】:

    标签: sql-server linq performance


    【解决方案1】:

    this post 中,Diego Frata 概述了一个 hack,它使时间戳可以从 LINQ 查询。

    诀窍是定义一个 Compare 方法,该方法采用两个 System.Data.Linq.Binary 参数

    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;
    

    (如果是job.TimeStamp>lastTimestamp)

    编辑: 如果您需要它在 SQL 之外工作,请参阅 Rory MacLeod's answer 以了解该方法的实现。

    【讨论】:

    • 在实体框架 (4.3.1) 中也可以使用,使用 Byte[] 而不是 Binary 作为比较器。
    • Diego 个人资料和博客的链接不再有效。
    【解决方案2】:

    SQL Server“时间戳”只是记录已更改的指示符,它实际上并不是日期/时间的表示。 (虽然假设每次修改数据库中的记录时都会增加,

    注意它会回零(不经常,诚然),所以唯一安全的测试是该值是否已更改,而不是它是否大于某个任意先前值。

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

    【讨论】:

    • 如果只需要测试值是否发生变化,可以将Binary实例与“a == b”进行比较
    • 感谢您的帖子。我现在对为什么不应该尝试重新利用我的 RowVer 专栏有了更好的理解。
    • 其实时间戳列对于这个目的是非常实用的。除非您对单个表进行超过 2^64 次修改,否则您无需担心它的包装。作为参考,如果每秒写入 1,000,000 次,则需要将近 600,000 年。
    • @Josh 时间戳/行版本跨越整个数据库,而不仅仅是表。
    【解决方案3】:

    // 选择自我们上次插入/更新以来发生变化的所有记录。

    有没有更好的解决方法?

    为什么不设置两列,一列用于 createddate,另一列用于 lastmodifieddate。我会说这是处理这种情况的更传统方式。

    【讨论】:

    • 感谢您的帖子。它让我更多地思考这个问题,意识到日期时间要好得多,因为它代表了更有用的信息。现在我可以检测记录的陈旧程度如何,而不仅仅是它是否陈旧。
    • 别忘了为他们添加索引
    • 日期/时间字段以及使用它们进行比较的问题是时钟变化可能来自不同的客户端或在数据库之间同步数据时,没有单一的时间来源。简单的时间戳(行版本)通常是检测更改的简单有效的方法。
    【解决方案4】:

    jaraics' answer 开始,您还可以为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 的使用取自 this answer 到关于比较字节数组的问题。如果数组的长度不同,但较长的数组与较短的数组以相同的字节开头,则认为较长的数组大于较短的数组,即使额外的字节全为零。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-22
      • 1970-01-01
      • 2010-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多