以二进制形式插入和选择 UUID(16)

Inserting and selecting UUIDs as binary(16)

我不明白为什么

SELECT UUID();

Returns 类似于:

3f06af63-a93c-11e4-9797-00505690773f

但是如果我将它插入一个二进制 (16) 字段(UUID() 函数),例如一个 BEFORE INSERT 触发器和 运行 一个 select,它 returns类似于:

0782ef48-a439-11

注意这两个UUID不是同一个数据

我意识到二进制和 UUID 字符串看起来不一样,但是 selected 数据不应该至少一样长吗?否则它怎么可能是唯一的?

存成char(36)是不是更好?我只需要它是唯一的以防止重复插入。它从不 select 编辑或用于联接。

编辑:

之前触发器会是这样的:

BEGIN

if NEW.UUID IS NULL THEN

NEW.UUID = UUID();

END IF

END

因此,作为对评论的回应。将 36 字符 UUID 存储为 binary(16) 的正确方法是以如下方式执行插入:

INSERT INTO sometable (UUID) VALUES
       (UNHEX(REPLACE("3f06af63-a93c-11e4-9797-00505690773f", "-","")))

UNHEX 因为 UUID 已经是一个十六进制值。我们 trim (REPLACE) 语句中的破折号将长度减少到 32 个字符(我们的 16 个字节表示为 HEX)。显然,您可以在存储它之前的任何时候执行此操作,因此它不必由数据库处理。

您可以像这样检索 UUID:

SELECT HEX(UUID) FROM sometable;

以防万一有人遇到这个话题并且不确定它是如何工作的。

请记住:如果您使用 UUID 选择行,在条件 :

上使用 UNHEX()
SELECT * FROM sometable WHERE UUID = UNHEX('3f06af63a93c11e4979700505690773f');

literal notation(如 Alexis Wilke 所述):

SELECT * FROM sometable WHERE UUID = 0x3f06af63a93c11e4979700505690773f;

而不是HEX()列:

SELECT * FROM sometable WHERE HEX(UUID) = '3f06af63a93c11e4979700505690773f';

最后一个解决方案虽然可行,但需要 MySQL HEX 读取所有 UUID,然后才能确定哪些行匹配。效率很低。

编辑:如果您使用的是 MySQL 8,您应该查看 SlyDave 的回答中提到的 UUID 函数。这个答案仍然是正确的,但它没有优化可以使用这些函数本机完成的 UUID 索引。如果你在 < MySQL 8 上,你可以实现 Devon 的 polyfill,它提供与 MySQL.

以前版本相同的功能

从 MySQL 8 开始,您可以使用两个新的 UUID functions:

  • BIN_TO_UUID

    SELECT BIN_TO_UUID(uuid, true) AS uuid FROM foo;
    -- 3f06af63-a93c-11e4-9797-00505690773f
    
  • UUID_TO_BIN

    INSERT INTO foo (uuid) VALUES (UUID_TO_BIN('3f06af63-a93c-11e4-9797-00505690773f', true));
    

此方法还支持重新排列 uuid 的时间部分以增强索引性能(通过按时间顺序排列),只需将第二个参数设置为 true - 这仅适用于 UUID1。

如果您在 UUID_TO_BIN 标志上使用 true 来提高索引性能(推荐),您还必须在 BIN_TO_UUID 上设置它,否则它无法正确转换回来。

有关详细信息,请参阅文档。

我正在使用 MariaDB,所以 BIN_TO_UUID 函数系列不存在。无论如何,我设法得到了相应的值。

bin -> hex

这里,uuid是一个uuid的binary(16)值;您将使用下面的值来 SELECT 它的可读版本。

LOWER(CONCAT(
    SUBSTR(HEX(uuid), 1, 8), '-',
    SUBSTR(HEX(uuid), 9, 4), '-',
    SUBSTR(HEX(uuid), 13, 4), '-',
    SUBSTR(HEX(uuid), 17, 4), '-',
    SUBSTR(HEX(uuid), 21)
))

hex -> bin

此处,cc6e6d97-5501-11e7-b2cb-ceedca613421 是 UUID 的可读版本,您将在 WHERE 子句中使用下面的值来查找它。

UNHEX(REPLACE('cc6e6d97-5501-11e7-b2cb-ceedca613421', '-', ''))

干杯

其他答案正确。 UUID() 函数 returns 一个 36 个字符的字符串,需要使用显示的函数(UNHEX() 或在较新的平台上,UUID_TO_BIN())进行转换。

但是,如果您使用自己的软件创建 UUID,则可以改用 Hexadecimal Literal notation

所以我会在 MySQL UUID() 函数中使用以下内容:

INSERT INTO sometable (id) VALUES (UNHEX(REPLACE(UUID(), '-', '')));  -- all versions
INSERT INTO sometable (id) VALUES (UUID_TO_BIN(UUID());               -- since v8.0

但如果我生成自己的 UUID,请使用它;

INSERT INTO sometable (id) VALUES 0x3f06af63a93c11e4979700505690773f;

同样,您可以在 WHERE 子句中使用十六进制文字:

SELECT * FROM sometable WHERE id = 0x3f06af63a93c11e4979700505690773f;

如果您不必每次都将数据转换为 UUID 字符串,这样会更快。

注意:'0xaBc中的'x'区分大小写。然而,十六进制数字不是。

BIN_TO_UUID 的 Polyfill 和 MySQL 的 UUID_TO_BIN 5 以及 swap_flag 参数。

DELIMITER $$

CREATE FUNCTION BIN_TO_UUID(b BINARY(16), f BOOLEAN)
RETURNS CHAR(36)
DETERMINISTIC
BEGIN
   DECLARE hexStr CHAR(32);
   SET hexStr = HEX(b);
   RETURN LOWER(CONCAT(
        IF(f,SUBSTR(hexStr, 9, 8),SUBSTR(hexStr, 1, 8)), '-',
        IF(f,SUBSTR(hexStr, 5, 4),SUBSTR(hexStr, 9, 4)), '-',
        IF(f,SUBSTR(hexStr, 1, 4),SUBSTR(hexStr, 13, 4)), '-',
        SUBSTR(hexStr, 17, 4), '-',
        SUBSTR(hexStr, 21)
    ));
END$$


CREATE FUNCTION UUID_TO_BIN(uuid CHAR(36), f BOOLEAN)
RETURNS BINARY(16)
DETERMINISTIC
BEGIN
  RETURN UNHEX(CONCAT(
  IF(f,SUBSTRING(uuid, 15, 4),SUBSTRING(uuid, 1, 8)),
  SUBSTRING(uuid, 10, 4),
  IF(f,SUBSTRING(uuid, 1, 8),SUBSTRING(uuid, 15, 4)),
  SUBSTRING(uuid, 20, 4),
  SUBSTRING(uuid, 25))
  );
END$$

DELIMITER ;

--
-- Tests to demonstrate that it works correctly. These are the values taken from
-- https://dev.mysql.com/doc/refman/8.0/en/miscellaneous-functions.html#function_uuid-to-bin
--
-- If you run these SELECTs using the above functions, the 
-- output of the two columns should be exactly identical in all four cases.
SET @uuid = '6ccd780c-baba-1026-9564-5b8c656024db';
SELECT HEX(UUID_TO_BIN(@uuid, 0)), '6CCD780CBABA102695645B8C656024DB';
SELECT HEX(UUID_TO_BIN(@uuid, 1)), '1026BABA6CCD780C95645B8C656024DB';
SELECT BIN_TO_UUID(UUID_TO_BIN(@uuid,0),0), '6ccd780c-baba-1026-9564-5b8c656024db';
SELECT BIN_TO_UUID(UUID_TO_BIN(@uuid,1),1), '6ccd780c-baba-1026-9564-5b8c656024db';

包括来自 https://dev.mysql.com/doc/refman/8.0/en/miscellaneous-functions.html#function_uuid-to-bin that demonstrate that the above code returns the exact same results as the 8.0 function. These functions are considered DETERMINISTIC as they always produce the same output for a given input. See https://dev.mysql.com/doc/refman/8.0/en/create-procedure.html

的 SELECT 个样本

在 MySQL 4.0 及更高版本中,您可以像使用 MID

一样更改 UUID 的大小
SELECT MID(UUID(),1,32); # 32 characters long UUID
SELECT MID(UUID(),1,11); # 11 characters long UUID

正如@nickdnk 所指出的,您不应该这样做。 UUID 的总长度使它们独一无二。剥离其中的一部分可能会导致非唯一值。