В настоящее время мне поручено преобразовать кучу кода C# CLR в хранимые процедуры и скалярные функции SQL Server. На первый взгляд задача кажется достаточно простой, пока я не наткнулся на фрагмент кода, который генерирует новый Guid с массивом байтов в качестве параметра.
Код выглядит следующим образом:
public static Guid GenerateGuid()
{
var bytes = GenerateBytes();
return new Guid(bytes);
}
Сгенерированные байты здесь не имеют значения. Меня озадачивает то, как C# использует этот массив байтов для создания нового экземпляра Guid. Однако в SQL Server есть только одна функция NEWID(), которая не принимает параметров, она похожа на Guid.NewGuid().
К сожалению, это не то, что мне нужно для данного варианта использования.
Итак, я хотел бы спросить, как я могу переписать приведенный выше код C# в T-SQL, чтобы сгенерировать новое руководство с указанным массивом байтов вместо просто совершенно случайного руководства, используя NEWID()?
Похоже, что приведенная выше ссылка касается форматирования представления уникальных идентификаторов. Имейте в виду, что двоичная сериализация уникальных идентификаторов, вероятно, не совсем такая, как вы ожидаете, например: declare @foo uniqueidentifier = 'a89b1acd-9501-6ae6-b9c8-aabb07da2010'; select @foo, cast(@foo as varbinary(max));





Правильное преобразование массива байтов (BINARY(16) в SQL) в Guid (UNIQUEIDENTIFIER) зависит от того, организован ли ваш массив байтов с первыми тремя частями (одним 4-байтовым целым числом и двумя 2-байтовыми целыми числами) на хосте с прямым порядком байтов. порядке байтов или в сетевом порядке байтов с обратным порядком байтов. Если вам необходимо обеспечить совместимость и согласованность между платформами, вам необходимо знать, в какой форме находятся ваши данные, чтобы выбрать правильное преобразование.
Если байты уже расположены в порядке байтов хоста, как в случае с результатами C# Guid.ToByteArray(), преобразование SQL Server выполняется просто:
DECLARE @HostOrderBytes BINARY(16) = 0x33221100554477668899aabbccddeeff
DECLARE @Result UNIQUEIDENTIFIER = CONVERT(UNIQUEIDENTIFIER, @HostOrderBytes)
SELECT @Result -- 00112233-4455-6677-8899-aabbccddeeff
Однако если массив байтов имеет сетевой порядок байтов, преобразование требует некоторой перестановки данных перед преобразованием. В частности, байты с 1 по 4, 5 и 6, а также 7 и 8 необходимо поменять местами, чтобы разместить данные в порядке хоста.
Для этого выполните следующие действия:
DECLARE @NetworkOrderBytes BINARY(16) = 0x00112233445566778899aabbccddeeff
DECLARE @HostOrderBytes BINARY(16) =
SUBSTRING(@NetworkOrderBytes,4,1) -- Reverse bytes 1-4 (0-3)
+ SUBSTRING(@NetworkOrderBytes,3,1)
+ SUBSTRING(@NetworkOrderBytes,2,1)
+ SUBSTRING(@NetworkOrderBytes,1,1)
+ SUBSTRING(@NetworkOrderBytes,6,1) -- Reverse bytes 5-6 (4-5)
+ SUBSTRING(@NetworkOrderBytes,5,1)
+ SUBSTRING(@NetworkOrderBytes,8,1) -- Reverse bytes 7-8 (6-7)
+ SUBSTRING(@NetworkOrderBytes,7,1)
+ SUBSTRING(@NetworkOrderBytes,9,8) -- Remaining bytes are unchanged
DECLARE @Result UNIQUEIDENTIFIER = CONVERT(UNIQUEIDENTIFIER, @HostOrderBytes)
SELECT @Result -- 00112233-4455-6677-8899-aabbccddeeff
Альтернативой является преобразование байтов в шестнадцатеричную строку, вставка соответствующих дефисов, а затем преобразование в UNIQUEIDENTIFIER. Последний шаг этого подхода решает проблемы порядка байтов.
DECLARE @NetworkOrderBytes BINARY(16) = 0x00112233445566778899aabbccddeeff
DECLARE @Result UNIQUEIDENTIFIER =
CONVERT(UNIQUEIDENTIFIER,
STUFF(STUFF(STUFF(STUFF(
CONVERT(VARCHAR(50), @NetworkOrderBytes, 2),
9 , 0, '-'), 14, 0, '-'), 19, 0, '-'), 24, 0, '-'))
SELECT @Result -- 00112233-4455-6677-8899-aabbccddeeff
См. эту db<>fiddle для демонстрации.
Соответствующее обсуждение порядка байтов см. в разделе Алгоритм сортировки GUID SQL Server. Почему?.
@Charlieface - Хороший улов. Я обновил свой ответ. (Мой предыдущий опыт работы с этим заключался в реализации UUID на основе имени SHA-1 RFC 4122 версии 5, который требовал замены байтов для получения правильного ответа для каждого примера данных RFC. Мне не приходило в голову, что массив байтов C# уже может быть быть в порядке байтов хоста.)
Возможно, stackoverflow.com/a/1390129/4503491 пригодится?