Преобразование MySQLGuid (из Devart dotConnect для MySQL) в строку в SQL

Я использую продукт Devart DotConnect для подключения приложения .net к базе данных MySQL. Все работает довольно хорошо, но у меня возникла проблема при работе с SQL в базе данных.

Приложение использует Guids для идентификаторов строк в базе данных (изначально исходящих из приложения SQL Server), которые хранятся в базе данных как BINARY(16) (и используют класс DevArt MySQLGuid при доступе через приложение .net)

Проблема в том, что когда я хочу запросить в базе данных определенную строку, я не могу просто вставить строковое представление GUID в базу данных, поэтому я пытаюсь разработать функцию для преобразования двоичного представления GUID к строке.

Сначала я думал, что будет достаточно просто вызвать HEX(id) в запросе, а затем, если мне нужен дружественный читаемый вывод, все, что мне нужно сделать, это добавить - в соответствующем месте.

Оператор select может выглядеть так (на практике я бы обернул его как функцию):

 LOWER(CONCAT(LEFT(HEX(theme_id), 8), '-', MID(HEX(theme_id), 9,4), '-', MID(HEX(theme_id), 13,4), '-', MID(HEX(theme_id), 17,4), '-', RIGHT(HEX(theme_id), 12)))

не возвращает правильный GUID. Например, если я сохраняю d1dfd973-fa3d-4b90-a1eb-47217162cd40, то приведенный выше оператор выбора возвращает 73d9dfd1-3dfa-904b-a1eb-47217162cd40

Похоже, что порядок первых 8 байтов в группе был обратным (взяв первую часть строки GUID, мы имеем 73d9dfd1, что с точки зрения порядка байтов (т. е. обработки групп из двух символов как одного байта) равно d1dfd973, то есть правильный выход.

Мой вопрос таков: есть ли операция, которую я могу выполнить в диалекте SQL MySQL, которая позволит мне изменить порядок байтов соответствующих разделов? Я мог бы сделать более сложный оператор LEFT/MID/RIGHT, но мне кажется, что это не лучший способ.

Предложения будут очень признательны.


person Richard Comish    schedule 21.08.2014    source источник


Ответы (2)


Например, если я сохраняю d1dfd973-fa3d-4b90-a1eb-47217162cd40, то указанный выше оператор select возвращает 73d9dfd1-3dfa-904b-a1eb-47217162cd40.

Я думаю, что вы страдаете от проблем с прямым порядком байтов/обратным порядком байтов с вашими GUID. GUID см. в разделе «Двоичное кодирование» на вики-странице: http://en.wikipedia.org/wiki/Globally_unique_identifier

GUID и UUID представляют собой 16-байтовые значения, разбитые на 4 блока:

Data1 : 4 byte int : big endian for UUID, little endian for ms/x86 GUIDs
Data2 : 2 byte int : big endian for UUID, little endian for ms/x86 GUIDs
Data3 : 2 byte int : big endian for UUID, little endian for ms/x86 GUIDs
Data4 : 16 bytes   : stored in the same order for both UUIDs and GUIDs

GUID и UUID обычно записываются в виде шестнадцатеричных строк с использованием дефисов для разделения компонентов данных. Вот в чем фишка, строки как UUID, так и GUID записываются с данными 1-3 в обратном порядке. Одни и те же строки могут быть представлены разными шаблонами байтов в зависимости от того, хранятся ли они в UUID или GUID на платформе с прямым порядком байтов.

Давайте разберем четырехбайтовый блок Data1 вашего образца GUID: d1dfd973. Если эта строка представляет GUID на платформе Microsoft/Intel, то байты будут отображаться в памяти в следующем порядке:

guid[0] = 0x73 // LSB first
guid[1] = 0xd9
guid[2] = 0xdf
guid[3] = 0xd1 // MSB last

Я думаю, что это, вероятно, порядок байтов, который Деварт написал в базе данных. Затем ваш код пытается извлечь это с помощью

LEFT(HEX(theme_id), 8)

который создаст строку 73d9dfd1, потому что он просто использует данные в том порядке, в котором они были сохранены.

Есть ключ к тому, что я на правильном пути: ваш код читает Data4 в правильном порядке (a1eb-47217162cd40 в вашем примере GUID). Данные4 хранятся в одном и том же порядке байтов независимо от платформы и от того, говорим ли мы об UUID или GUID.

У вас есть несколько вариантов

  • Укажите, что все должно храниться в базе данных как GUID с прямым порядком байтов. Используйте процедуру преобразования, такую ​​как Andrzej, чтобы преобразовать обратно в строковое представление.

  • Мандат на сохранение всего — это UUID с обратным порядком байтов. Запустите однократное преобразование, чтобы изменить порядок байтов существующих строк. Вы можете использовать процедуры Анджея для преобразования, но после этого вы можете напрямую преобразовать двоичную форму в строковую.

person bigh_29    schedule 08.01.2015
comment
Я думаю, что вы совершенно правы - большое спасибо, я определенно могу внести изменения и работать с этим. Большое спасибо за вашу помощь, и очень очень хорошее место! - person Richard Comish; 09.01.2015

шаг 1)

Я создаю таблицу в Mysql

CREATE TABLE `table_code` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `code` varchar(45) DEFAULT NULL,
  `guid` binary(16) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;

шаг 2)

Я вставляю одну строку:

INSERT INTO table_code (code,guid) 
VALUES ('d1dfd973fa3d4b90a1eb47217162cd40',UNHEX('d1dfd973fa3d4b90a1eb47217162cd40'));

шаг 3)

я бегу

SELECT 
id,
code,
LOWER(CONCAT(LEFT(HEX(guid), 8),'-', MID(HEX(guid), 9,4), '-',MID(HEX(guid), 13,4), '-', MID(HEX(guid), 17,4), '-', RIGHT(HEX(guid), 12))),
LOWER(HEX(guid))

FROM test.table_code;

шаг 4) Результат:

1   d1dfd973fa3d4b90a1eb47217162cd40    d1dfd973-fa3d-4b90-a1eb-47217162cd40 d1dfd973fa3d4b90a1eb47217162cd40

Все выглядит нормально - на стороне Mysql.

Итак, вывод: Вы уверены, что вводите 'd1dfd973fa3d4b90a1eb47217162cd40' (это следует делать с помощью UNHEX('d1dfd973fa3d4b90a1eb47217162cd40')) или другое (обратное) значение.

Во всяком случае, чтобы превратить такую ​​строку: "73d9dfd13dfa904ba1eb47217162cd40" в эту "d1dfd973-fa3d-4b90-a1eb-47217162cd40"

Вы можете использовать такой код:

LOWER(CONCAT(
MID(HEX(guid), 7,2),
MID(HEX(guid), 5,2),
MID(HEX(guid), 3,2),
LEFT(HEX(guid), 2),'-',
MID(HEX(guid), 11,2),
MID(HEX(guid), 9,2),'-',
MID(HEX(guid), 15,2),
MID(HEX(guid), 13,2),'-',
MID(HEX(guid), 17,4), '-', 
RIGHT(HEX(guid), 12)))

Я думаю, что Mysql не дает нам более простого способа, потому что нет такой функции, работающей с двоичными данными.

Чтобы проверить это:

Шаг 5)

INSERT INTO table_code (code,guid) 
VALUES ('73d9dfd13dfa904ba1eb47217162cd40',UNHEX('73d9dfd13dfa904ba1eb47217162cd40'));

шаг 6)

    SELECT 
    id,
    code as `raw code`,
    LOWER(CONCAT(LEFT(HEX(guid), 8),'-', MID(HEX(guid), 9,4), '-',MID(HEX(guid), 13,4), '-', MID(HEX(guid), 17,4), '-', RIGHT(HEX(guid), 12))),
    LOWER(HEX(guid)) `decoded`,
LOWER(CONCAT(
MID(HEX(guid), 7,2),
MID(HEX(guid), 5,2),
MID(HEX(guid), 3,2),
LEFT(HEX(guid), 2),'-',
MID(HEX(guid), 11,2),
MID(HEX(guid), 9,2),'-',
MID(HEX(guid), 15,2),
MID(HEX(guid), 13,2),'-',
MID(HEX(guid), 17,4), '-', 
RIGHT(HEX(guid), 12))) as switched

    FROM test.table_code;

шаг 7) Кажется, все в порядке.

person Andrzej Reduta    schedule 30.08.2014
comment
Спасибо - я просто проверяю решение. Единственное, что меня беспокоит, это то, что REVERSE кажется строковой функцией, что означает, что все символы будут перевернуты, тогда как я хочу перевернуть пары символов (или перевернуть байты) - person Richard Comish; 31.08.2014
comment
К сожалению, это не работает - поскольку я подозреваю, что это переворачивает строку, а не байты. Я не могу найти эквивалент REVERSE на уровне байтов в MySQL. - person Richard Comish; 02.09.2014
comment
Неправильно понятый. Я изменил весь ответ. - person Andrzej Reduta; 02.09.2014
comment
Большое спасибо - это работает хорошо для меня. К сожалению, у меня нет контроля над сериализацией GUID в MySQL (это делается провайдером DevArt MySQL. Я не уверен, почему они делают это именно так, но, вероятно, есть причина). В любом случае, благодарю Вас. Я отмечу это как решение. - person Richard Comish; 02.09.2014