【问题标题】:Comparing timestamp values in one database比较一个数据库中的时间戳值
【发布时间】:2014-02-12 15:23:31
【问题描述】:

我正在使用 SQL Server 或 Azure SQL 数据库(取决于环境)。

在数据库内部,我有一些表使用数据类型 timestamp 进行版本控制,其中一些没有使用它。

例如:表AB 有列timestamp ver,而表CD 没有这样的列。

我正在使用实体框架。我能以某种方式安全地将这个值(匹配到byte[])转换为uint64吗?是否保证在转换更高的值之后修改后面的行:如果我从表 A 中获取行 a 并从表 B a.convertedVer > b.convertedVer 中获取行 a.convertedVer > b.convertedVer 当且仅当 a 被修改时晚于b

我需要处理请求:“给我来自A 的所有行匹配某事并且ver 高于myVer”。

【问题讨论】:

  • byte[] 有什么问题?您不必检查该值是否更高,只要它相等即可。无论如何,是的,时间戳总是一个 8 字节的值,所以UInt64 应该没问题。
  • 我更新了问题。我需要处理请求:给我所有来自 A 匹配的行并且 ver 高于 myVer 的行。

标签: sql sql-server entity-framework azure-sql-database


【解决方案1】:

根据 Gert Arnold 的回答,可以保证较高的值是后面的行被修改。

创建将byte[8] 转换为uint64 的代码并不难。我们可以使用BitConverter,或者我们可以简单地添加移位了0、8、16、24、32、...的字节。

最后一个问题:如何在Entity Framework 中创建查询Where RowVersion > someUInt64

几个事实:

  1. MS SQL 理解Where RowVersion > someUInt64Where RowVersion > someBytes
  2. Azure SQL 理解 Where RowVersion > someUInt64Where RowVersion > someBytes
  3. C# 不能比较 byte[]byte[] 也不能比较 byte[]uint64

因此即使数据库能够处理这些请求,我们也无法在C# Entity Framework 中创建此类请求。

我们需要C# 来理解比较byte[]byte[]byte[]uint64。第一种情况更容易。我们可以轻松地创建比较byte[]byte[] 的方法。

public static int Compare(this byte[] b1, byte[] b2)
{
    throw new NotImplementedException();
}

我们不需要实现它。 T-SQL 已经实现了。我们需要做的就是让C# 编译我们使用Compare 方法的代码。

现在我们可以将查询转换为:Where(v => v.Compare(someBytes) > 0)

【讨论】:

  • 不错!我为自己的答案添加了一些背景。您可以将您的答案标记为已接受,以引导未来的读者找到解决方案。
【解决方案2】:

是否保证转换较高的值后修改后面的行?

是的,来自MSDN

rowversion 数据类型只是一个 递增 数字 (...) 每个数据库都有一个计数器,对于在包含数据库中的 rowversion 列的表。

(我的重点)

顺便提一下,timestamp 已被弃用。目前只是rowversion的同义词。

我能以某种方式安全地将这个值(匹配到 byte[])转换为 uint64 吗?

是的,但是不知何故这个词很讨厌。非常讨厌。

在 T-SQL 中,您可以编写如下查询

SELECT * FROM MyTable WHERE rowversion > @someRowversion

如您所知,实体框架将rowversion 作为byte[8] 返回。字节数组不可比较(没有自定义比较器)。所以我们不能写:

db.MyTable.Where(t => t.RowVersion > someByteArray);

它无法编译。如果只有 EF 可以绕过 C# 编译器并将表达式转换为 SQL 就好了! (当然它永远不会)。

现在呢?

假设您有一个转换器,可以将byte[] 转换为UInt64。 (这个转换器可以使用BitConverter,但有一些字节顺序的细节我现在不讨论)。你不会写

db.MyTable.Where(t => MyConverter.Convert(t.RowVersion) > someUInt64);

EF 将反对它无法将MyConverter.Convert(t.RowVersion) 转换为 SQL。和

db.MyTable.AsEnumerable()
          .Where(t => MyConverter.Convert(t.RowVersion) > someUInt64);

不是一个选项,因为它会将所有MyTable 拉入内存。

也许我遗漏了一些非常明显的东西(我希望如此),但比较 EF LINQ 查询中的 rowversion 值似乎是一条死胡同。我认为你最好使用存储过程或视图来实现你想要的。

编辑

所以是的!幸运的是,我遗漏了一些东西,但不是太明显。正如 Ari 所解释的,可以创建一个 method stub Compare EF 在将表达式转换为 SQL 时提取该方法。它从未在 CLR 中执行。

提供更多背景信息:在 EF 源代码中,所有内容都在 LinqExpressionNormalizer 中。方法VisitMethodCall() 查找多个方法名称,如果在表达式中找到这些方法名称,它们将转换为表达式并与包含的表达式合并。这些方法是

  • 等于(静态):Object.Equals(x, y)
  • 比较字符串 (VB):x = y
  • 比较(静态):Class.Compare(x, y)
  • 等于(实例):x.Equals(y)
  • CompareTo(实例):x.CompareTo(y)
  • 包含(实例):List<T> x.Contains(y)

【讨论】:

  • 我发现如果我为byte[] 定义Compare 方法做任何事情(例如抛出NotImplementedException)我可以写.Where(t => t.RowVersion.Compare(myVer) > 0)。这很丑陋,但可以按预期工作。我认为MS应该将rowversion实现为uint64
  • 嗯,这很酷!作为对您自己问题的回答,您介意简要解释一下这种方法吗?
猜你喜欢
  • 2015-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-12
  • 2016-12-12
  • 1970-01-01
  • 2013-01-16
相关资源
最近更新 更多