将数字转换为Rowversion / Timestamp

时间:2011-06-29 22:16:29

标签: sql sql-server tsql sql-server-2008

使用SQL Server 2008 +。

我有一个rowversion列(也就是时间戳),我从数据库中检索并使用以下代码转换为数字(20,0):

CONVERT(NUMERIC(20,0), RowVersionColumn + 0) AS [RowVersion]    

稍后,我需要将该数值(存储在ADO.NET DataSet中作为ulong / UInt64),并将其转换回rowversion数据类型。

但这会产生问题。看起来好像你可以转换为数字(20,0),反向操作无法产生正确的值。例如:

DECLARE @MyNumericValue NUMERIC(20,0)
DECLARE @MyRowVersionValue ROWVERSION

SET @MyNumericValue = 12345
SET @MyRowVersionValue = CONVERT(rowversion, @MyNumericValue)

PRINT CONVERT(NUMERIC(20,0), @MyRowVersionValue + 0)

运行此代码打印出值959447040,而不是12345.从CONVERT语句中删除“+ 0”会产生正确的结果,但我仍然无法可靠地获取曾经是rowversion的数值把它变回一个rowversion,其值应该是它应该是。

以下是此问题的更好演示:

DECLARE @MyNumericValue NUMERIC(20,0)
DECLARE @MyRowVersionValue ROWVERSION
DECLARE @MyRowVersionValue2 ROWVERSION

SET @MyRowVersionValue = @@DBTS
SET @MyNumericValue = CONVERT(NUMERIC(20,0), @MyRowVersionValue + 0)
SET @MyRowVersionValue2 = CONVERT(rowversion, @MyNumericValue)

SELECT @MyRowVersionValue 
SELECT @MyNumericValue
SELECT @MyRowVersionValue2

您的结果将根据您的输入而有所不同,但作为示例,我的输出是:

0x0000000003ADBB2F
61717295
0x140000012FBBAD03

第一个和最后一个值应匹配,但它们不匹配。我猜这与二进制数据转换经常导致填充这一事实有关,而这个填充改变了值。参见:

http://msdn.microsoft.com/en-us/library/aa223991(v=SQL.80).aspx

思想?

编辑:澄清一下,我正在处理rowversion / timestamp的事实是偶然的。 BINARY(8)而不是ROWVERSION也会出现同样的问题,因为它们是等效的数据类型。

2 个答案:

答案 0 :(得分:2)

虽然我不明白为什么你会将无意义的ROWVERSION值转换为数字然后再转回,你是否尝试使用BIGINT - 它匹配UInt64所需的8个字节而不是可变字节数(取决于强制NUMERIC(20,0)的实际值会将其更改为?

CREATE TABLE dbo.rv(a INT, rv ROWVERSION);

INSERT dbo.rv(a) SELECT 1 UNION SELECT 2;

WITH x AS
(
    SELECT 
        rv, 
        rv_as_bigint = CONVERT(BIGINT, rv) 
    FROM dbo.rv
)
SELECT 
    rv, 
    rv_as_bigint, 
    rv_back_to_rv = CONVERT(ROWVERSION, rv_as_bigint) 
FROM x;

DROP TABLE dbo.rv;

或者拿走原始样本并将BIGINT换成NUMERIC(20,0):

DECLARE @MyNumericValue BIGINT
DECLARE @MyRowVersionValue ROWVERSION
DECLARE @MyRowVersionValue2 ROWVERSION

SET @MyRowVersionValue = @@DBTS
SET @MyNumericValue = CONVERT(BIGINT, @MyRowVersionValue) -- removed the +0 hack
SET @MyRowVersionValue2 = CONVERT(rowversion, @MyNumericValue)

SELECT @MyRowVersionValue 
SELECT @MyNumericValue
SELECT @MyRowVersionValue2

答案 1 :(得分:2)

当您将数据转换为binary时(rowversion基本上是binary(8)),您将获得其内部表示而非逻辑值。您可以使用以下代码段验证行为:

DECLARE @MyNumericValue NUMERIC(20,0) = 0
SELECT @MyNumericValue, CONVERT(binary(8), @MyNumericValue)

或者你可以尝试

DECLARE @MyBinary binary(8) = 0x00
SELECT @MyBinary, CONVERT(numeric(20,0), @MyBinary)

根本不会转换。 那么,你的脚本真正发生的是:

SET @MyNumericValue = 12345
-- @MyRowVersionValue gets the internal representation of the numeric value 12345
SET @MyRowVersionValue = CONVERT(rowversion, @MyNumericValue)
-- +0 converts the binary value to int, then performs int-to-numeric conversion 
PRINT CONVERT(NUMERIC(20,0), @MyRowVersionValue + 0)
-- Real binary-to-numeric conversion.
PRINT CONVERT(NUMERIC(20,0), @MyRowVersionValue)

真正的二进制到数字转换是安全的,但在您的情况下,您可能希望将rowversion转换为bigint(或忘记数值并将其存储为二进制字符串)比numeric,因为CONVERT(NUMERIC(20,0), @MyRowVersionValue)并没有真正为您提供数值。