converting sql server rowversion to long or ulong?

后端 未结 4 761
隐瞒了意图╮
隐瞒了意图╮ 2020-12-18 21:14

What is the proper type for the rowversion (timestamp) data type?

I know it is 8 bytes but i cannot find a link in MSDN which tell if it is a signed or unsigned long

相关标签:
4条回答
  • 2020-12-18 21:54

    Short answer: it doesn't matter but I'd choose UInt64.

    Details: semantically it's equivalent to binary(8) so, strictly speaking, it's neither UInt64 nor Int64 but just a chunk of bytes (and in that way it should be managed). That said I'd choose UInt64 because it's an incrementing number to hold row version then (from a logic point of view) 0xFFFFFFFFFFFFFFFF should be greater than 0 and it's not true for Int64 (because 64 bits set to 1 give -1 and it's less than 0).

    Edit: note that, for reasons known only in the innest SQL Server designers circle, ROWVERSION is big-endian (while - obviously - bigint is not) then you first need to reverse bytes, see this answer for a nice implementation.

    0 讨论(0)
  • 2020-12-18 21:54

    Neither will work correctly for purposes of comparing timestamp/rowversion values, if you're running on an x86 family CPU, because of endian. The first byte of a timestamp is most significant, but not so for little endian integer types.

    Call Array.Reverse(ts) before calling BitConverter.ToUInt64(ts), and for the other direction, after calling BitConverter.GetBytes(tsUInt64)

    0 讨论(0)
  • 2020-12-18 21:57

    It does matter. You want your comparison to have the same result as SQL Server's comparison. SQL Server uses unsigned comparisons on binary types:

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

    If you do the same thing with long which is signed, 0xFFFFFFFFFFFFFFFF represents -1. That means your comparison will be incorrect; it won't match with the same comparison done in SQL Server.

    What you definitely want is to use ulong where 0xFFFFFFFFFFFFFFFF is ulong.MaxValue.

    Endianness is also important

    Additionally, as Mark pointed out, BitConverter.GetUInt64 is not converting properly. Mark is not completely right- BitConverter is either big-endian or little-endian depending on the system it's running on. You can see this for yourself. Also, even if BitConverter was always little-endian, Array.Reverse is less performant with a heap allocation and byte-by-byte copying. BitConverter is just not semantically or practically the right tool for the job.

    This is what you want:

    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];
    }
    

    The cleanest solution

    Here is the solution I use: Timestamp.cs.

    Basically once you cast to Timestamp, you can't go wrong.

    0 讨论(0)
  • 2020-12-18 22:02

    I use this (updated):

    private UInt64 GetUInt64ForRowVersion(byte[] rowVersion)
    {
        byte[] rr = (byte[])rowVersion.Clone();
        if (BitConverter.IsLittleEndian) { Array.Reverse(rr); }
        return BitConverter.ToUInt64(rr, 0);
    }
    
    0 讨论(0)
提交回复
热议问题