【问题标题】:converting sql server rowversion to long or ulong?将 sql server rowversion 转换为 long 或 ulong?
【发布时间】:2014-01-15 19:02:07
【问题描述】:

rowversion(时间戳)数据类型的正确类型是什么?

我知道它是 8 个字节,但我在 MSDN 中找不到一个链接来判断它是有符号长还是无符号长。

我应该使用哪个代码,这有关系吗?

byte[] SqlTimeStamp;

long longConversion;
longConversion = BitConverter.ToInt64(SqlTimeStamp,0);
TimeStamp = BitConverter.GetBytes(longConversion);

ulong ulongConversion;
ulongConversion = BitConverter.ToUInt64(SqlTimeStamp,0);
TimeStamp = BitConverter.GetBytes(ulongConversion);

【问题讨论】:

  • 我有点担心人们可能会在 unsigned 问题和 big-endian 问题上得到一些微妙的错误答案。请参阅my answer

标签: c# sql-server rowversion


【解决方案1】:

这很重要。您希望您的比较结果与 SQL Server 的比较结果相同。 SQL Server 对二进制类型使用无符号比较:

select case when 0x0FFFFFFFFFFFFFFF < 0xFFFFFFFFFFFFFFFF then 'unsigned' else 'signed' end

如果你用签名的long 做同样的事情,0xFFFFFFFFFFFFFFFF 代表-1。这意味着您的比较将不正确;它不会与在 SQL Server 中进行的相同比较匹配。

您绝对想要的是使用 ulong,其中 0xFFFFFFFFFFFFFFFFulong.MaxValue

字节顺序也很重要

此外,正如 Mark 指出的那样,BitConverter.GetUInt64 没有正确转换。 Mark 不完全正确- BitConverter 是大端或小端,具体取决于它所运行的系统。你可以see this for yourself。此外,即使 BitConverter 始终是 little-endian,Array.Reverse 在堆分配和逐字节复制方面的性能也较差。 BitConverter 只是在语义上或实际上不适合这项工作。

这就是你想要的:

static ulong BigEndianToUInt64(byte[] bigEndianBinary)
{
    return ((ulong)bigEndianBinary[0] << 56) |
           ((ulong)bigEndianBinary[1] << 48) |
           ((ulong)bigEndianBinary[2] << 40) |
           ((ulong)bigEndianBinary[3] << 32) |
           ((ulong)bigEndianBinary[4] << 24) |
           ((ulong)bigEndianBinary[5] << 16) |
           ((ulong)bigEndianBinary[6] <<  8) |
                   bigEndianBinary[7];
}

最干净的解决方案

更新:如果您使用 .NET Core 2.1 或更高版本(或 .NET Standard 2.1),您可以使用非常合适的 BinaryPrimitives.ReadUInt64BigEndian

在 .NET Framework 上,这是我使用的解决方案:Timestamp.cs。基本上一旦你投到Timestamp,你就不会出错。

【讨论】:

  • 我确实用“如果你在 x86 系列 CPU 上运行...”来限定它,所有这些都是小端序。
  • 我不希望有人认为这是正确的成语。它的内存和 CPU 效率较低,而且不太优雅。另外,最近有人会在大端 CPU 上复制它——最好不要一开始就将它耦合到 CPU。
  • @jnm2 我的 +1,你是对的,rowversion 存储为大端,您的 Timestamp 类为此目的非常整洁。我仍然想知道为什么 rowversion 是 b.e.而bigint 显然不是……
  • 谢谢,愉快的交谈。我猜到了原因,已经回复here
  • 它是大端的,而不是依赖于 CPU 的,这正是这里想要的。它更纯净。而且也不可能在服务器端做所有事情。例如,比较加载的实体。
【解决方案2】:

简短回答:没关系,但我会选择UInt64

详细信息:从语义上讲,它相当于binary(8),所以严格来说,它既不是UInt64,也不是Int64,而只是一个字节块(并且应该以这种方式对其进行管理)。那就是说我会选择UInt64,因为它是一个递增的数字来保存行版本然后(从逻辑的角度来看)0xFFFFFFFFFFFFFFFF应该大于0,而@则不是这样987654330@(因为 64 位设置为 1 给出-1 并且它小于0)。

编辑:请注意,由于只有内部 SQL Server 设计者圈子知道的原因,ROWVERSION 是大端(而 - 显然 - bigint 不是)那么你首先需要反向字节,见this answer for a nice implementation

【讨论】:

  • 这确实很重要!如果您不正确,您的比较结果将与 SQL Server 的比较不匹配。见this answer
  • 抱歉,这不是真的。 SQL Server 允许在binary 上使用&lt;&gt;。您的客户端逻辑应该完全匹配 SQL Server 的逻辑,否则它是错误的。此外,很多人在timestamp (binary(8)) 上进行过滤。这是一种既定的做法。我刚刚回答了一个关于它的不同问题,我正在第三次在企业项目中使用它。
  • 此外,即使 SQL Server 具有良好定义的无符号大端 binary 比较运算符这一事实并不能说服您,SQL Server 增加 binary(8) 的事实如何它是一个未签名的大端 int64 吗?在处理timestamp 列时,您肯定需要明确了解这一点。
  • 你这么说让我想起了一个人说 C# 中的数字 ID 应该禁止除 ==!= 之外的任何运算符。我很欣赏他所追求的。唯一的问题是,如果您需要获取每对唯一的 ID,&lt;&gt; 对于 ID 仍然非常有用。在这种情况下使用这些比较是绝对合法的。这是最纯粹的事情。 (希望我能找到博客文章的链接。)
  • 这是一个可能的逻辑原因。 Big-endian 二进制和字符串可以被同等对待并按字典顺序进行比较。最重要的是,如果 rowversion 是 bigint,它将被签名,这对于 rowversions 的简单比较是一个问题。这使得binary(字典比较)成为完美的选择,字典比较是大端的原因。
【解决方案3】:

如果您在 x86 系列 CPU 上运行,由于 endian,两者都无法正常工作以比较时间戳/行版本值。时间戳的第一个字节是最重要的,但对于小端整数类型而言并非如此。

在调用 BitConverter.ToUInt64(ts) 之前调用 Array.Reverse(ts),而对于另一个方向,在调用 BitConverter.GetBytes(tsUInt64) 之后调用

【讨论】:

  • 它们在 little endian CPU 上的重要性不高。这些值会随着更新的每一行而递增,因此如果您在执行更新后为给定的一组行保留 MAX(rowversion),则可以通过测试 rowversion > persistedrowversion 来确定哪些行已更新。只要您将值保留在 SQL 中,比较就会正确计算,但是如果您需要在应用程序代码中比较它们,例如 linq 查询,那么您必须修复字节顺序以匹配 CPU 字节序。
  • 另一个线程中的某个人有一个更优雅的解决方案,在 SQL 中定义一个计算列,将 bin(8) 转换为 big_int。可能会更快一些,而且应用程序代码肯定更少复杂。
  • 不错!也许并不总是适用但很好的解决方案。如果您仍然可以找到它,请在您的帖子中添加链接
  • BitConverter.ToUInt64 是 little-endian big-endian,具体取决于系统。最好以便携的方式进行。也更高效。见this answer
【解决方案4】:

我用这个(更新):

private UInt64 GetUInt64ForRowVersion(byte[] rowVersion)
{
    byte[] rr = (byte[])rowVersion.Clone();
    if (BitConverter.IsLittleEndian) { Array.Reverse(rr); }
    return BitConverter.ToUInt64(rr, 0);
}

【讨论】:

  • 最简单实用的答案。
  • 危险:使用建议的解决方案时 rowVersion 发生变异,这使 ChangeTracker 认为该项目已被修改!
  • @IgorB 数组是克隆的。什么是危险?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多