在 MariaDB 中,放置在触发器中的函数开始返回不同的值
Posted
技术标签:
【中文标题】在 MariaDB 中,放置在触发器中的函数开始返回不同的值【英文标题】:In MariaDB, function placed in a trigger starts returns a different value 【发布时间】:2013-10-02 18:06:05 【问题描述】:有这样的表和函数:
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 二进制分发版)
【问题讨论】:
【参考方案1】:表的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 |
+--------------------------------------+
我创建了一个小提琴here。
注意:
虽然CHARID
接受 NULL,但 ID
不接受,因此尝试在 CHARID
中插入 NULL 值会导致触发器尝试将 ID
设置为 NULL 并拒绝插入。
尝试在CHARID
中插入导致UUID_ENCODE
返回NULL 的无效值也会失败。
ID
的 DEFAULT 0
子句仅允许您从插入列表中省略列。写入表的值将始终是您的触发器生成的值。
始终为 NULL 的可空列每行应占用 0 到 2 个字节的额外存储空间,具体取决于您的表布局和行格式(例如,您需要避免 MyISAM 引擎的 FIXED
格式)。
可能有许多变化。如果您不介意额外的存储空间,您可以保留 CHARID
值而不将其设置为 NULL。如果您想允许显式插入二进制 ID
值,您可以向触发器添加一个检查,以便仅当 NEW.ID
为 0 时才计算它,等等。
如果允许,您应该为 UPDATE 操作设置类似的触发器。
【讨论】:
以上是关于在 MariaDB 中,放置在触发器中的函数开始返回不同的值的主要内容,如果未能解决你的问题,请参考以下文章
MariaDB 触发器在同一张表上执行 UPDATE 和 INSERT