我应该使用varchar(36),还是有更好的方法?
我会将其存储为 char(36)。
在ThaBadDawg的回答基础上,使用以下方便的函数(感谢我的一位更有经验的同事)将长度为36的字符串转换为16字节的字节数组。
DELIMITER $$
CREATE FUNCTION `GuidToBinary`(
$Data VARCHAR(36)
) RETURNS binary(16)
DETERMINISTIC
NO SQL
BEGIN
DECLARE $Result BINARY(16) DEFAULT NULL;
IF $Data IS NOT NULL THEN
SET $Data = REPLACE($Data,'-','');
SET $Result =
CONCAT( UNHEX(SUBSTRING($Data,7,2)), UNHEX(SUBSTRING($Data,5,2)),
UNHEX(SUBSTRING($Data,3,2)), UNHEX(SUBSTRING($Data,1,2)),
UNHEX(SUBSTRING($Data,11,2)),UNHEX(SUBSTRING($Data,9,2)),
UNHEX(SUBSTRING($Data,15,2)),UNHEX(SUBSTRING($Data,13,2)),
UNHEX(SUBSTRING($Data,17,16)));
END IF;
RETURN $Result;
END
$$
CREATE FUNCTION `ToGuid`(
$Data BINARY(16)
) RETURNS char(36) CHARSET utf8
DETERMINISTIC
NO SQL
BEGIN
DECLARE $Result CHAR(36) DEFAULT NULL;
IF $Data IS NOT NULL THEN
SET $Result =
CONCAT(
HEX(SUBSTRING($Data,4,1)), HEX(SUBSTRING($Data,3,1)),
HEX(SUBSTRING($Data,2,1)), HEX(SUBSTRING($Data,1,1)), '-',
HEX(SUBSTRING($Data,6,1)), HEX(SUBSTRING($Data,5,1)), '-',
HEX(SUBSTRING($Data,8,1)), HEX(SUBSTRING($Data,7,1)), '-',
HEX(SUBSTRING($Data,9,2)), '-', HEX(SUBSTRING($Data,11,6)));
END IF;
RETURN $Result;
END
$$
CHAR(16)
实际上是一个BINARY(16)
,选择你喜欢的类型即可。
为了更好地跟踪代码,请以数字顺序排列的 GUID 示例为例。(这里使用非法字符只是为了说明 - 每个位置都有一个唯一的字符。) 函数将转换字节顺序以实现更好的索引聚集。重新排序后的 GUID 如下所示:
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
78563412-BC9A-FGDE-HIJK-LMNOPQRSTUVW
去掉破折号后:
123456789ABCDEFGHIJKLMNOPQRSTUVW
78563412BC9AFGDEHIJKLMNOPQRSTUVW
GuidToBinary
($guid char(36)) RETURNS binary(16)
RETURN CONCAT(
UNHEX(SUBSTRING($guid, 7, 2)),
UNHEX(SUBSTRING($guid, 5, 2)),
UNHEX(SUBSTRING($guid, 3, 2)),
UNHEX(SUBSTRING($guid, 1, 2)),
UNHEX(SUBSTRING($guid, 12, 2)),
UNHEX(SUBSTRING($guid, 10, 2)),
UNHEX(SUBSTRING($guid, 17, 2)),
UNHEX(SUBSTRING($guid, 15, 2)),
UNHEX(SUBSTRING($guid, 20, 4)),
UNHEX(SUBSTRING($guid, 25, 12))); - Jonathan Oliverchar(36)是一个不错的选择。此外,可以使用MySQL的UUID()函数,该函数返回一个36个字符的文本格式(带有连字符的十六进制),可用于从数据库中检索此类ID。
"更好"取决于你所优化的方面。
你对存储大小/性能和开发易用性有多在意?更重要的是,请问您是否生成了足够多的GUID或者频繁地获取它们,这会产生影响吗?
如果答案是“没有”,那么char(36)
已经足够好了,并且它可以使存储/获取GUID变得非常简单。否则,binary(16)
也是合理的选择,但您需要依靠MySQL和/或您选择的编程语言来进行字符串表示形式与二进制之间的相互转换。
使用二进制(16)会更好,比使用varchar(32)更好。
KCD发布的GuidToBinary例程应该进行调整,以考虑GUID字符串中时间戳的位布局。如果该字符串表示版本1 UUID,例如由uuid() mysql例程返回的那些,则时间组件嵌入在字母1-G中,不包括D。
12345678-9ABC-DEFG-HIJK-LMNOPQRSTUVW
12345678 = least significant 4 bytes of the timestamp in big endian order
9ABC = middle 2 timestamp bytes in big endian
D = 1 to signify a version 1 UUID
EFG = most significant 12 bits of the timestamp in big endian
select uuid(), 0
union
select uuid(), sleep(.001)
union
select uuid(), sleep(.010)
union
select uuid(), sleep(.100)
union
select uuid(), sleep(1)
union
select uuid(), sleep(10)
union
select uuid(), 0;
/* output */
6eec5eb6-9755-11e4-b981-feb7b39d48d6
6eec5f10-9755-11e4-b981-feb7b39d48d6
6eec8ddc-9755-11e4-b981-feb7b39d48d6
6eee30d0-9755-11e4-b981-feb7b39d48d6
6efda038-9755-11e4-b981-feb7b39d48d6
6f9641bf-9755-11e4-b981-feb7b39d48d6
758c3e3e-9755-11e4-b981-feb7b39d48d6
前两个UUID在时间上最接近。它们仅在第一个块的最后3个半字节中有所不同。这些是时间戳的最低有效位,这意味着在将其转换为可索引的字节数组时,我们希望将它们向右移动。例如,最后一个ID是最新的,但KCD的交换算法会将其放在第三个ID之前(从第一个块中的最后几个字节开始,3e在dc之前)。
索引的正确顺序应该是:
1e497556eec5eb6...
1e497556eec5f10...
1e497556eec8ddc...
1e497556eee30d0...
1e497556efda038...
1e497556f9641bf...
1e49755758c3e3e...
我建议使用以下函数,因为@bigh_29提到的函数将我的GUID转换为了新的GUID(原因我不理解)。另外,在我对表格进行的测试中,这些函数稍微快一点。 https://gist.github.com/damienb/159151
DELIMITER |
CREATE FUNCTION uuid_from_bin(b BINARY(16))
RETURNS CHAR(36) DETERMINISTIC
BEGIN
DECLARE hex CHAR(32);
SET hex = HEX(b);
RETURN LOWER(CONCAT(LEFT(hex, 8), '-', MID(hex, 9,4), '-', MID(hex, 13,4), '-', MID(hex, 17,4), '-', RIGHT(hex, 12)));
END
|
CREATE FUNCTION uuid_to_bin(s CHAR(36))
RETURNS BINARY(16) DETERMINISTIC
RETURN UNHEX(CONCAT(LEFT(s, 8), MID(s, 10, 4), MID(s, 15, 4), MID(s, 20, 4), RIGHT(s, 12)))
|
DELIMITER ;
select CAST("hello world, this is as long as uiid" AS BINARY(16));
产生结果:hello world, thi
- MD004