插入php后获取生成的uuid

Posted

技术标签:

【中文标题】插入php后获取生成的uuid【英文标题】:Get the generated uuid after insert php 【发布时间】:2011-02-08 12:58:50 【问题描述】:

我有一个表字段类型 varchar(36),我想通过 mysql 动态生成它,所以我使用了以下代码:

$sql_code = 'insert into table1 (id, text) values (uuid(),'some text');';
mysql_query($sql_code);

如何在插入记录后立即检索生成的 uuid?

【问题讨论】:

您的 id 字段是唯一的吗? @Pekka:你相信在我们的宇宙中有可能发生 UUID 碰撞吗? ;-) @zerkms no :) 但 IIRC,LAST_INSERT_ID() 仅适用于唯一列,因此我的问题 @Pekka:更重要的是,它只适用于自动增量,iirc 是定义唯一的。 @zerkms 啊,很公平。 【参考方案1】:
    char(36) 更好

    你不能。唯一的解决方案是执行 2 个单独的查询:

    SELECT UUID() INSERT INTO table1 (id, text) VALUES ($uuid, 'text')

其中 $uuid 是第一步检索到的值。

【讨论】:

char(36) 并没有好多少,binary(16) 效率最高 @pospi: char(36) 比 varchar 好。使用二进制你会遇到字符串比较问题(二进制不尊重排序规则) 这是正确的,但是如果您要插入二进制数据,那么您就不会将其视为字符串或期望比较像那样工作 - 您将使用 HEX() 和使用十六进制表示法或 UNHEX()... 进行查询,并且您在字段之间进行的任何比较都将在原始二进制数据而不是其字符上完成(: 我已经扩展了我原来的答案here (: CHAR(36) 在 UTF-8 中变为 3*36 字节长,加上长度字节。所以你最好的选择是(A)二进制,(B)ASCII CHAR,或(C)VARCHAR【参考方案2】:

您可以使用 SQL 触发器做所有您需要做的事情。下面的SQL在tablename.table_id上添加了一个触发器,在插入的时候自动创建主键UUID,然后将新创建的ID存入一个SQL变量供以后检索:

CREATE TRIGGER `tablename_newid` 
AFTER INSERT ON `tablename` 
FOR EACH ROW 
BEGIN 
    IF ASCII(NEW.table_id) = 0 THEN 
        SET NEW.table_id = UNHEX(REPLACE(UUID(),'-','')); 
    END IF; 
    SET @last_uuid = NEW.table_id; 
END

作为奖励,它将二进制形式的 UUID 插入到 binary(16) 字段中,以节省存储空间并大大提高查询速度。

编辑:触发器应该在插入它自己的 UUID 之前检查现有的列值,以便模仿在 MySQL 中为表主键提供值的能力 - 如果没有这个,任何传入的值将始终被覆盖触发。该示例已更新为使用 ASCII() = 0 检查 INSERT 中是否存在主键值,这将检测二进制字段的空字符串值。

编辑 2:在评论 here 之后,有人向我指出,即使行插入失败,使用 BEFORE INSERT 也会设置 @last_uuid 变量。我已更新我的答案以使用AFTER INSERT - 虽然我觉得在一般情况下这是一种完全好的方法,但它可能在集群或复制数据库下存在行复制问题。如果有人知道,我也很乐意!

要读取新行的插入 ID,只需运行 SELECT @last_uuid

在查询和读取此类二进制值时,MySQL 函数 HEX()UNHEX() 将非常有用,以十六进制表示法(前面为 0x)编写查询值也将非常有用。鉴于将这种类型的触发器应用于 table1,您原始答案的 php 端代码将是:

// insert row
$sql = "INSERT INTO table1(text) VALUES ('some text')";
mysql_query($sql);

// get last inserted UUID
$sql = "SELECT HEX(@last_uuid)";
$result = mysql_query($sql);
$row = mysql_fetch_row($result);
$id = $row[0];

// perform a query using said ID
mysql_query("SELECT FROM table1 WHERE id = 0x" . $id);

跟进回复@ina's comment:

UUID 不是字符串,即使 MySQL 选择这样表示它。它是原始形式的二进制数据,这些破折号只是 MySQL 向您表示它的友好方式。

UUID 最有效的存储方式是将其创建为 UNHEX(REPLACE(UUID(),'-','')) - 这将删除该格式并将其转换回二进制数据。这些函数会使原始插入速度变慢,但您在该键或列上执行的所有后续比较在 16 字节二进制字段上将比 36 字符字符串快得多。

一方面,字符数据需要解析和本地化。进入查询引擎的任何字符串通常都会根据数据库的字符集自动进行整理,并且某些 API(想到 wordpress)甚至在查询之前对所有字符串数据运行 CONVERT()。二进制数据没有这种开销。另一方面,您的 char(36) 实际上分配了 36 个字符,这意味着(如果您的数据库是 UTF-8)每个字符可能与 3 or 4 bytes 一样长,具体取决于您的 MySQL 版本使用。因此,char(36) 的范围可以从 36 个字节(如果它完全由低位 ASCII 字符组成)到 144 个字节(如果它完全由高位 UTF8 字符组成)。这比我们为二进制字段分配的 16 个字节大很多

对此数据执行的任何逻辑都可以使用UNHEX() 完成,但最好通过简单地将查询中的数据转义为十六进制并以0x 为前缀来完成。这与读取字符串一样快,即时转换为二进制并直接分配给相关查询或单元格。非常快。 读取数据稍慢 - 如果您的客户端 API 不能很好地处理二进制数据(特别是 PHP 通常会确定二进制字符串=== null 并且如果在没有先调用bin2hex()base64_encode() 或类似方法的情况下进行操作,则会破坏它们) - 但这种开销与字符排序规则一样小,更重要的是仅在实际单元格上调用SELECTed ,并非所有单元都参与查询结果的内部计算。

因此,当然,所有这些小幅度的速度提升都非常小,而其他领域的速度则小幅下降 - 但是当您将它们全部加起来时,binary 仍然是最重要的,当您考虑用例和一般“读取”时> 写的原则真的很闪耀。

...这就是为什么binary(16)char(36) 更好。

【讨论】:

显示 UUID 不仅是 mysql 的决定,也是通过推荐来表示的。 即便如此,它仍然只是为了表示。所有这些破折号都表示 UUID 的哪些部分是从时间戳生成的,哪些部分保留了唯一性,哪些部分基于主机的 MAC 地址。您没有理由需要从 UUID 中知道任何这些信息,也没有理由无论您是否知道正在生成的字节偏移和 variant of UUID 都无法弄清楚。如果所有删除它们所做的任何事情都会进一步混淆任何从该 ID 派生的信息。 “实际上分配了 36 个字符,这意味着(如果您的数据库是 UTF-8)每个字符 3 或 4 个字节”——这是错误的。用于 ascii 安全字符的 UTF-8 字符串将占用 36 个字节 我已经参考BEFORE INSERT 触发器上的一些 cmets 更新了我的答案,请参阅 edit2 注释(:【参考方案3】:

其实很简单 你可以把它传给mysql,它会返回插入的id。

set @id=UUID();
insert into <table>(<col1>,<col2>) values (@id,'another value');
select @id;

【讨论】:

插入失败会怎样?你仍然得到返回的id吗?它是否掩盖了插入失败?我知道这个答案很旧。只是发布这个以防有人仍然偶然发现它(我做到了)。 @Cully,这取决于您使用的库或语言。如果插入失败,一些语言会抛出异常,在你的代码中,你需要捕获异常,一些框架只会抛出错误字符串作为输出。所以很大程度上取决于你用来访问数据库的框架跨度> 【参考方案4】:

根据 uuid() 函数的实现方式,这是非常糟糕的编程习惯 - 如果您尝试在启用二进制日志记录的情况下执行此操作(即在集群中),则插入将 most likely fail。 Ivan 的建议看起来可能会解决眼前的问题——但我认为这只会返回为自动增量字段生成的值——确实是 what the manual says。

另外,使用 uuid() 有什么好处?它的生成计算成本很高,需要大量存储空间,增加了查询数据的成本并且在密码学上不安全。请改用序列生成器或自动增量。

无论您使用序列生成器还是 uuid,如果您必须将其用作数据库上唯一的唯一键,那么您需要先分配值,将其读回 phpland 并将值嵌入/绑定为后续插入查询的文字。

【讨论】:

嗯,这不是一个坏习惯。这只是解决问题的另一种方法:codinghorror.com/blog/2007/03/… 还有 PK 和加密有什么关系?! 当然二进制日志不会对 uuid 造成任何问题,无论是在集群中还是在复制中。 我已经用 MySQL Cluster 和 Gallera 测试了上面写的触发器 - 没有问题。它处理 INSERT 触发器上存在 UUID 键的事实意味着负责生成行的节点在运行其事务时创建键,然后该键出现在提供给其他节点的数据中级联到复制的表。

以上是关于插入php后获取生成的uuid的主要内容,如果未能解决你的问题,请参考以下文章

mybatis中useGeneratedKeys用法--插入数据库后获取主键值

[PHP] ubuntu下使用uuid扩展获取uuid

如何将 NEWID() / GUID / UUID 插入代码编辑器?

生成 v4 UUID 的 PHP 函数

PHP生成 uuid

php 纯PHP UUID生成器