1

有这样的表和函数:

CREATE TABLE `test` (`ID` BINARY(16)) ENGINE=InnoDB;

CREATE FUNCTION `UUID_ENCODE`(`uuid` CHAR(36))
    RETURNS binary(16)
    DETERMINISTIC
BEGIN
    RETURN UNHEX(REVERSE(REPLACE(uuid,'-','')));
END

CREATE FUNCTION `UUID_DECODE`(`uuid` BINARY(16))
    RETURNS char(36)
    DETERMINISTIC
BEGIN
RETURN LOWER(CONCAT_WS('-',
    REVERSE(HEX(RIGHT(uuid,6))),
    REVERSE(HEX(MID(uuid,9,2))),
    REVERSE(HEX(MID(uuid,7,2))),
    REVERSE(HEX(MID(uuid,5,2))),
    REVERSE(HEX(LEFT(uuid,4)))
));
END

插入工作正常:

INSERT INTO test (ID) VALUES(UUID_ENCODE('323febe6-cd89-4773-a46c-aab794fb7cbc'));

SELECT UUID_DECODE(ID) FROM test;
+--------------------------------------+
| uuid_decode(id)                      |
+--------------------------------------+
| 323febe6cd89-4773-a46c-aab7-94fb7cbc |
+--------------------------------------+

只需创建一个触发器:

CREATE TRIGGER `test_uuid_encode` BEFORE INSERT ON `test` FOR EACH ROW BEGIN
    SET NEW.ID = UUID_ENCODE(NEW.ID);
END

并在表中获得一半截断值:

INSERT INTO test (ID) VALUES('323febe6-cd89-4773-a46c-aab794fb7cbc');

Warning (Code 1265): Data truncated for column 'ID' at row 1

SELECT UUID_DECODE(ID) FROM test;
+--------------------------------------+
| uuid_decode(id)                      |
+--------------------------------------+
| 000000000000-0000-0032-3feb-e6cd8947 |
+--------------------------------------+

这个触发器有什么问题?

软件版本为:

版本 5.5.33a-MariaDB for Win64 on x86(mariadb.org 二进制发行版)

4

1 回答 1

2

您的表的ID列被声明为BINARY(16),并且您UUID_ENCODE期望CHAR(36). 当您在UUID_ENCODE没有触发器的情况下直接调用您的函数时,它会正确接收您的 36 个字符的字符串。当您改用触发器时,您插入的值首先转换为列的类型,因此NEW.ID将包含CAST('323febe6-cd89-4773-a46c-aab794fb7cbc' AS BINARY(16). 当触发器调用函数时,它会再次将值NEW.ID转换为函数所期望的类型。因此,这是您的函数将收到的值:

SELECT CAST(CAST('323febe6-cd89-4773-a46c-aab794fb7cbc' AS BINARY(16)) AS CHAR);

323febe6-cd89-47

如您所见,您的函数接收截断的值。您得到的结果相当于:

SELECT UUID_DECODE(UUID_ENCODE('323febe6-cd89-47'));

000000000000-0000-0032-3feb-e6cd8947

更新

在触发器中实现所需功能的一种方法是添加一个具有函数期望类型的虚拟可空列:

CREATE TABLE test (ID BINARY(16) KEY DEFAULT 0, CHARID CHAR(36) NULL);

CREATE TRIGGER test_uuid_encode BEFORE INSERT ON test FOR EACH ROW BEGIN
    SET NEW.ID = UUID_ENCODE(NEW.CHARID)
      , NEW.CHARID = NULL;
END

这样你就可以做到:

INSERT test (CHARID) VALUES ('323febe6-cd89-4773-a46c-aab794fb7cbc');

SELECT UUID_DECODE(ID) FROM test;
+--------------------------------------+
| uuid_decode(id)                      |
+--------------------------------------+
| 323febe6cd89-4773-a46c-aab7-94fb7cbc |
+--------------------------------------+

我在这里创建了一个小提琴。

注意:

  • 虽然CHARID接受 NULL,ID但不接受,因此尝试插入 NULL 值CHARID将导致触发器尝试设置ID为 NULL 并拒绝插入。
  • 尝试将无效值插入CHARID导致UUID_ENCODE返回 NULL 的操作也将失败。
  • DEFAULT 0for 子句ID仅允许您从插入列表中省略该列。写入表的值将始终是触发器生成的值。
  • 始终为 NULL 的可空列每行应占用 0 到 2 个字节的额外存储空间,具体取决于您的表布局和行格式(例如,您需要避免FIXED使用 MyISAM 引擎的格式)。
  • 有许多可能的变化。如果您不介意额外的存储空间,您可以保留该CHARID值而不将其设置为 NULL。如果您想允许显式插入二进制ID值,您可以向触发器添加一个检查,以便NEW.ID仅计算它是否为 0 等。
  • 如果允许,您应该对 UPDATE 操作有类似的触发器。
于 2013-09-28T20:02:56.270 回答