每次调用UUID()函数时,它本身都会产生不同的值,正如我所期望的那样:
SELECT UUID() from INFORMATION_SCHEMA.TABLES LIMIT 3;
3bb7d468-f9c5-11e9-8349-d05099466715
3bb7d482-f9c5-11e9-8349-d05099466715
3bb7d492-f9c5-11e9-8349-d05099466715但是,一旦我们在REPLACE()函数中使用它,它就开始产生相同的值:
SELECT REPLACE(UUID(),'-','-') from INFORMATION_SCHEMA.TABLES LIMIT 3;
e0f2d47a-f9c5-11e9-8349-d05099466715
e0f2d47a-f9c5-11e9-8349-d05099466715
e0f2d47a-f9c5-11e9-8349-d05099466715在这样的Select语句中,我们期望插入的每一行都有一个唯一的值:
INSERT INTO MyTable (uid, tableName) -- uid is binary(16)
SELECT UNHEX(REPLACE(UUID(),'-','')), TABLE_NAME from INFORMATION_SCHEMA.TABLES;注意,为了方便起见,我使用了信息模式的表列表。这不重要,但是对于那些好奇的人,我们的PK是二进制(16)形式的UUID。我改变不了这一点,请不要把注意力集中在这上面。
UUID()函数是非确定性的,而REPLACE()函数是确定性的.我本以为UUID()函数的非确定性特性会导致REPLACE()函数的行为就好像它对每一行都有不同的参数一样,但是似乎DB引擎通过假设UUID()是常数来进行优化。
我还用另一个非确定性函数RAND()测试了这种行为,在本例中,REPLACE()函数如我们所期望的那样工作!
SELECT REPLACE(RAND(),' ',' ') from INFORMATION_SCHEMA.TABLES LIMIT 3;
0.911571646026868
0.626416072832808
0.6977608461843439问题
是否有方法执行"Insert From Select“,并在select中的每一行生成二进制16格式的唯一UUID?
为什么会发生这种情况?这是个虫子吗?
更新
我在当地使用5.7.27:
mysql Ver 14.14 Distrib 5.7.27, for Linux (x86_64)
但这将最终部署到AWS RDS实例中。哈哈..。Terraform (脚本部署)使用5.7.16引擎版本编制了一个AWS RDS实例。
在AWS控制台中,我看到了5.7.26版( 5.7版)和8.0.16版( 8.0版)的支持。我将讨论如何升级已部署的引擎版本。如@Schwern所建议的,我希望将PK列定义更改为默认值。
围绕着工作
在让其他人同意修改版本之前,我将使用临时表作为生成的id值的中间存储。
CREATE TEMPORARY TABLE GeneratedIds (
generatedId varchar(36) NOT NULL,
tableName text NOT NULL
);
INSERT INTO GeneratedIds (generatedId, tableName)
SELECT UUID(), TABLE_NAME from INFORMATION_SCHEMA.TABLES;
INSERT INTO MyTable (uid, tableName) -- uid is binary(16)
SELECT UNHEX(REPLACE(generatedId,'-','')), tableName FROM GeneratedIds;
DROP TABLE GeneratedIds;这不是很优雅,但确实有效。在我的例子中,我是在一个sql迁移文件中工作的,在这个文件中,我可以以一种内聚的方式将这种sql序列串在一起。我不建议在代码中这样做;它闻起来很难闻。
结论
,这确实是MySQL中的一个bug,,我快速搜索了他们的bug DB,但是我没有发现提到它。不管怎么说,上面的SQL语句说明了这个缺陷,@Schwern和我已经证明这个错误已经在5.7.27版本(确切地)和8.0.16版本(可能所有的8..,只测试了8.0.16和8.0.18)中得到了修复。
8.0.16版本测试:
Server version: 8.0.16 MySQL Community Server - GPL
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> SELECT REPLACE(UUID(),'-','-') from INFORMATION_SCHEMA.TABLES LIMIT 3;
+--------------------------------------+
| REPLACE(UUID(),'-','-') |
+--------------------------------------+
| 96f9205a-fdc6-11e9-87de-d05099466715 |
| 96f920f9-fdc6-11e9-87de-d05099466715 |
| 96f9213e-fdc6-11e9-87de-d05099466715 |
+--------------------------------------+
3 rows in set (0.00 sec)发布于 2019-10-29 03:27:19
MySQL 5.6.46、5.7.28和8.0.18似乎没有这个问题。如果可以的话升级。
升级的好处之一是,您现在可以将函数用作列默认值。这允许您将UUID设置为主键的默认设置,从而避免此问题和许多其他问题。您还可以使用uuid_to_bin和bin_to_uuid。
create table MyTable (
uid binary(16) primary key default(uuid_to_bin(uuid())),
tableName text not null
);
INSERT INTO MyTable (tableName)
SELECT TABLE_NAME from INFORMATION_SCHEMA.TABLES;如果你不能升级,你可以使用触发器设置默认主键,如下所示。
https://stackoverflow.com/questions/58600623
复制相似问题