Конвертировать числовые значения в Rowversion / Timestamp - PullRequest
1 голос
/ 30 июня 2011

Использование 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. Удаление «+ 0» из оператора CONVERT приводит к правильному результату, но я все еще не могу надежно принять числовое значение, которое раньше было строкой и превратить его обратно в версию строки, значение которой должно быть таким.

Ниже приведена еще лучшая демонстрация этой проблемы:

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 ]

2 голосов
/ 30 июня 2011

Когда вы конвертируете данные в 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)

Реальное преобразование двоичного числа в числовое безопасно, но в вашем случае вы можете преобразовать преобразование строки в bigintили забудьте о числовом значении и просто сохраните его как двоичную строку), а не numeric, поскольку CONVERT(NUMERIC(20,0), @MyRowVersionValue) на самом деле не получает числовое значение.

2 голосов
/ 30 июня 2011

Хотя я не понимаю, почему вы должны преобразовывать бессмысленное значение ROWVERSION в число, а затем обратно, пробовали ли вы использовать BIGINT - который соответствует 8 байтам, требуемым UInt64, а не переменному количеству байт (в зависимости от фактическое значение), что принудительно 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...