插入并选择二进制 UUID(16)

问题描述 投票:0回答:6

我不明白为什么

SELECT UUID();

返回类似:

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

但是,如果我使用 BEFORE INSERT 触发器将其插入二进制(16)字段(UUID()函数)并运行选择,它会返回类似以下内容:

0782ef48-a439-11

注意,这两个UUID不是相同的数据。

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

将其存储为 char(36) 是否更好?我只需要它是唯一的以防止重复插入。它永远不会被选择或用于连接。

编辑:

触发之前会是这样的:

BEGIN

if NEW.UUID IS NULL THEN

NEW.UUID = UUID();

END IF

END
mysql binary uuid
6个回答
135
投票

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

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

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

您可以像这样检索 UUID:

SELECT HEX(UUID) FROM sometable;

以防万一有人遇到此线程并且不确定它是如何工作的。

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

UNHEX()
:

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

文字符号(如 Alexis Wilke 提到的):

SELECT * FROM sometable WHERE UUID = 0x3f06af63a93c11e4979700505690773f;

而不是

HEX()
在专栏上:

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

最后一个解决方案虽然有效,但要求 MySQL

HEX
先检查所有 UUID,然后才能确定哪些行匹配。效率太低了

编辑:如果您使用的是 MySQL 8,您应该查看 SlyDave 的回答中提到的 UUID 函数。这个答案仍然是正确的,但它没有优化 UUID 索引,而 UUID 索引可以使用这些函数本机完成。如果你在 < MySQL 8 or MariaDB, you can implement Devon's polyfill, which provides identical functionality on previous versions of MySQL.


64
投票

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

  • 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。

如果您在

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

请参阅文档了解更多详细信息。


22
投票

使用 swap_flag 参数对 MySQL 5 或 MariaDB 的 BIN_TO_UUID 和 UUID_TO_BIN 进行 Polyfill。

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 的 SELECT 示例,证明上述代码返回与以下完全相同的结果8.0功能。这些函数被认为是确定性的,因为它们对于给定的输入总是产生相同的输出。请参阅https://dev.mysql.com/doc/refman/8.0/en/create-procedure.html


16
投票

我正在使用 MariaDB,因此

BIN_TO_UUID
函数系列不存在。无论如何我设法得到了相应的值。

bin -> 十六进制

这里,

uuid
是uuid的二进制(16)值;您将使用下面的值来选择它的可读版本。

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)
))

十六进制 -> bin

此处,

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

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

干杯


16
投票

其他答案都是正确的。

UUID()
函数返回 36 个字符的字符串,需要使用所示函数进行转换(
UNHEX()
,或者在较新的平台上,
UUID_TO_BIN()
)。

但是,如果您使用自己的软件创建 UUID,则可以使用 十六进制文字表示法

所以我将使用以下 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 字符串,那么速度会更快。

注意:

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


-2
投票

在 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 的总长度使它们独一无二。剥离其中的一部分可能会导致非唯一值。

© www.soinside.com 2019 - 2024. All rights reserved.