我的问题很简单。 我知道UUID的概念,我想生成一个引用UDB的"存储"中的每个"项目"。 看起来合理吧?
问题是以下行返回错误:
1 2 3 4 5 6
| honeydb=# INSERT INTO items VALUES(
uuid_generate_v4(), 54.321, 31, 'desc 1', 31.94);
ERROR: FUNCTION uuid_generate_v4() does NOT exist
LINE 2: uuid_generate_v4(), 54.321, 31, 'desc 1', 31.94);
^
HINT: No FUNCTION matches the given name AND argument types. You might need TO ADD explicit TYPE casts. |
我已在以下位置阅读该页面:http://www.postgresql.org/docs/current/static/uuid-ossp.html
我在Ubuntu 10.04 x64上运行Postgres 8.4。
-
Postgres本机支持UUID作为数据类型,甚至能够被索引并用作主键。 但是要生成UUID值(例如为列建立默认值),则需要Postgres扩展名(插件)。 Postgres的许多内部版本(发行版)都包含此类扩展名,但并未激活该扩展名。 请参阅Craig Ringer的正确答案,以了解如何激活它。
-
如果您安装了uuid-ossp并且仍然出现此错误,请尝试使用您的架构名称为函数添加前缀,例如 select dbo.uuid_generate_v4()
uuid-ossp是一个contrib模块,因此默认情况下不会加载到服务器中。您必须将其加载到数据库中才能使用。
对于现代PostgreSQL版本(9.1及更高版本),这很容易:
1
| CREATE EXTENSION IF NOT EXISTS"uuid-ossp"; |
但对于9.0及以下版本,您必须改为运行SQL脚本来加载扩展。请参阅8.4中的contrib模块文档。
对于Pg 9.1及更高版本,请阅读当前的contrib文档和CREATE EXTENSION。这些功能在9.0或更早的版本(例如8.4)中不存在。
如果您使用的是PostgreSQL的打包版本,则可能需要安装包含contrib模块和扩展名的单独软件包。在软件包管理器数据库中搜索" postgres"和" contrib"。
-
我应该键入:pg_config --sharedir来找到我的共享目录以便导入模块,但是该命令在Shell或psql命令提示符下均不起作用。
-
所以我不得不运行sudo apt-get install libpq-dev来运行pg_config --sharedir,但是现在我的sharedir中没有contrib文件夹,根据postgresql.org/docs/8.4/static/contrib,该文件夹应该存在.html
-
@advocate您正在使用发行版打包的PostgreSQL,因此您应该只能使用apt-get install postgresql-contrib或类似名称。尝试apt-cache search postgresql |grep contrib查找所需的程序包名称。
-
sudo apt-get install postgresql-contrib已成功运行。然后我必须运行psql -d dbname -f SHAREDIR / contrib / module.sql,现在它可以工作了!!!选择uuid_generate_v1();现在返回1。非常感谢!
-
创建扩展名" uuid-ossp";在postgresql-9.1中也不起作用,错误:无法打开扩展名控制文件" /usr/pgsql-9.1/share/extension/uuid-ossp.control":创建扩展名时不会出现此类文件或目录。你能帮我我所缺少的吗,我的PostgreSQL版本是9.1。
-
@SatishSharma请发布一个新问题。如果您认为与此相关,请链接回此页面。包括详细信息,例如如何安装Pg,操作系统等。
-
请注意,如果不安装postgresql-contrib软件包,则会收到错误:错误:无法打开扩展控制文件" /usr/share/postgresql/9.3/extension/uuid-ossp.control":没有此类文件或目录
-
@DrewNoakes是的,因此是最后一段。
-
我在错误字符串在Google上删除后发布了该评论。此外,它至少为Ubuntu提供了一个特定的程序包名称。
-
如果导入的数据库扩展中已经包含uuid-ossp,则uuid_generate_v4()可能无法正常工作。如果是这种情况,只需删除扩展名,然后再次创建它即可。
-
使用IF NOT EXISTS是个好习惯,尤其是在任何sql脚本中-如果您重新运行该脚本或将其转移到已经加载了该脚本的另一个系统,它可以防止发出错误。 (我只是将其添加到答案中。)
没有扩展名(作弊)
1 2 3
| SELECT uuid_in(md5(random()::text || clock_timestamp()::text)::cstring);
output>> c2d29867-3d0b-d497-9191-18a9d8ee7830 |
(至少在8.4中有效)
-
感谢@Erwin Brandstetter提供的clock_timestamp()解释。
如果您需要有效的v4 UUID
1
| SELECT uuid_in(overlay(overlay(md5(random()::text || ':' || clock_timestamp()::text) placing '4' FROM 13) placing to_hex(FLOOR(random()*(11-8+1) + 8)::INT)::text FROM 17)::cstring); |

*感谢@Denis Stafichuk @Karsten和@autronix
另外,在现代Postgres中,您可以简单地进行以下转换:
SELECT md5(random()::text || clock_timestamp()::text)::uuid
-
这比uuid-ossp好吗? uuid_in似乎为相同的输入返回相同的输出。因此,如果使用它在相同的准确时间(或random()具有相同的结果)创建两个UUID,则它们将相等。
-
要跟进您的PS,请执行以下操作:SELECT uuid_in(md5(random()::text || now()::text)::cstring);
-
@MattDiPasquale也许在任何意义上都不比使用uuid-ossp"更好",但是Im例如在PostgreSQL实例上工作,而我没有足够的特权来安装扩展。
-
仅有时间的明确问题是"如果时间还没有改变怎么办?"将该语句两个放入选择中,您将获得两次相同的结果。
-
@JosephLennox:在这两种情况下,clock_timestamp()都是更好的选择。与now()或CURRENT_TIMESTAMP不同,它是易失性的,并返回实际的当前时间。 SELECT uuid_in(md5(random()::text || clock_timestamp()::text)::cstring);另外,在现代Postgres中,您可以简单地强制转换:SELECT md5(random()::text || clock_timestamp()::text)::uuid-不需要更多的魔法。用例:stackoverflow.com/a/8335376/939860
-
不。如果这样做确实奏效的话。一个UUID有一种格式,它不仅是随机的十六进制字符,而且第三个组的第一个数字是实例的uuid版本(通常是4天)。如果您的应用程序检查该数字以查看其处理的uuid版本,并进行相应的操作,则它将在您的代码中失败。
-
@Tuncay Gnco?lu:生成有效的v4 UUID非常简单(尽管字符串覆盖方法浪费了2位随机性):select overlay(overlay(md5(random()::text || : || clock_timestamp()::text) placing 4 from 13) placing 8 from 17)::uuid;
-
@ Karsten,Ive通过恢复2位randmoness改进了您的示例:overlay(overlay(md5(random()::text || : || clock_timestamp()::text) placing 4 from 13) placing floor(random()*(11-8+1) + 8)::text from 17)::uuid;这应该补偿版本和变体,同时仍保留变体部分中的2 LSB 10xx。
-
@autronix,实际上您已经破解了它。您需要将floor(random()*(11-8+1) + 8)转换为十六进制以使其正确。继承人更正的版本:select overlay(overlay(md5(random()::text || : || clock_timestamp()::text) placing 4 from 13) placing to_hex(floor(random()*(11-8+1) + 8)::int)::text from 17)::uuid;
-
@ZuzEL:您应该删除最重要的答案,因为如果使用now(),发生冲突的可能性很高,那么在进行事务处理时生成多个uuid时,它很容易出错。请使用clock_timestamp来避免这种情况。
-
我编辑过的@JohnRumpel。感谢您关注该碰撞问题。
-
当尝试在Java中解析由此生成的UUID,然后调用uuid.timestamp()时,它抱怨uuid不是基于时间的uuid,因此使用此uuid有点麻烦。
-
@RobinJonsson没什么可恶的。 UUID的版本4不是基于时间的。 en.wikipedia.org/wiki/
-
在答案中提到的任何方法中,使用clock_timestamp都没有意义,因为无论如何数据都会馈入md5,因此会丢失。因此,我们可以简单地使用md5(random()::text || random()::text)::uuid:使用random(返回double precision,即64位)两次可以给我们128位随机信息,该信息通过md5转换为不同(但仍然是随机)的128位数字的十六进制表示。 。
Craig Ringer的答案是正确的。这是Postgres 9.1及更高版本的更多信息……
可以使用分机吗?
如果已为Postgres安装(您的集群使用Postgres术语)构建了扩展名,则只能安装该扩展名。例如,我发现uuid-ossp扩展包含在EnterpriseDB.com提供的Mac OS X安装程序的一部分中。几十个扩展中的任何一个都可用。
要查看uuid-ossp扩展在Postgres集群中是否可用,请运行以下SQL查询pg_available_extensions系统目录:
1
| SELECT * FROM pg_available_extensions; |
安装扩展
要安装与UUID相关的扩展,请使用此SQL中所示的CREATE EXTENSION命令:
1
| CREATE EXTENSION IF NOT EXISTS"uuid-ossp"; |
请注意:尽管有相反的说明,但我发现仍需要在扩展名周围使用QUOTATION MARK字符。
SQL标准委员会或Postgres团队为此命令选择了一个奇怪的名称。在我看来,他们应该选择"安装扩展"或"使用扩展"之类的东西。
验证安装
您可以通过运行以下SQL查询pg_extension系统目录来验证扩展已成功安装在所需的数据库中:
1
| SELECT * FROM pg_extension; |
UUID作为默认值
有关更多信息,请参见Postgres中的"问题:UUID的默认值"列
旧方法
上面的信息使用了Postgres 9.1中新增的扩展功能。在以前的版本中,我们必须在.sql文件中查找并运行脚本。添加了扩展功能,以简化安装,为扩展的创建者交易了更多的工作,而减少了扩展的用户/用户的工作。请参阅我的博客文章以获取更多讨论。
UUID的类型
顺便说一句,Question中的代码调用了函数uuid_generate_v4()。这将生成一种称为版本4的类型,其中几乎所有128位都是随机生成的。尽管这对于限制在较小的行集上的有限使用是很好的,但是如果您想消除冲突的可能性,请使用UUID的另一个"版本"。
例如,原始版本1将主机的MAC地址与当前日期时间和任意数字结合在一起,发生冲突的机会几乎为零。
有关更多讨论,请参见我对相关问题的回答。
-
如果您不确定并且不想检查(例如在脚本中),也可以使用CREATE EXTENSION IF NOT EXISTS ...
-
版本4 UUID几乎适用于任何大小的数据集,不仅是"在较小的行集上的有限使用"。在大约85年的时间里,您必须每秒生成10亿个UUID(或大约4500万兆字节的数据,比当今最大的数据库大数千倍),甚至有50%的冲突机会。除非您是国家安全局(NSA),否则第4版适用于任何目的。另一方面,版本1遭受MAC地址被顺序分配(并且经常被欺骗或不可用)的事实,这就是为什么引入更高版本的原因之一。
-
@Jazz我看不到顺序分配的MAC或欺骗的MAC与生成UUID有何关联,除非您使用的MAC在与使用UUID相同的上下文中被另一台计算机欺骗,否则欺骗的MAC是无关紧要的。
-
@Jazz对于基于随机的UUID,关于UUID不可靠的错误信息和误解足以让我想减轻任何疑虑。虽然我同意,对于大多数实际目的,使用正确构造的生成器生成的版本4 UUID足够了,但仍有一些冲突的机会。使用其他版本(例如版本1)实际上消除了碰撞的机会。这就是为什么它首先被发明的原因。
-
@Jazz至于在版本1之后创建的其他版本,我仅听说其动机是出于对披露MAC(可能对网络黑客有用)(MAC所隐含的位置)的隐私和安全隐患的担忧,和日期时间。
-
@BasilBourque v1的问题不是正确实现时发生冲突的概率,而是错误实现时的概率。正如Wikipedia所说:"版本1和2 UUID的唯一性……还取决于网卡制造商是否为其卡正确分配了唯一的MAC地址,这与其他制造过程一样容易出错。"同样,在某些容器化或虚拟化环境中,来自底层硬件的真实MAC地址也不可用。如果许多容器具有相同的MAC但具有自己的clockseq计数器,则它们的v1 UUID可能会冲突。
-
但是,v1中的@BasilBourque弱点并不是我评论的重点。您最初的答案暗示v4不适合大型数据集,因为发生碰撞的可能性比v1高。尽管很难计算v1的碰撞概率,但这种方法具有误导性,而且可能是错误的,因为它非常依赖于实现。
-
@BasilBourque例如,node-uuid项目计算出它们的clockseq计数器与4.6e18中的1相同的概率(这样两个进程将生成相同的v1 UUID序列)。是的,这是微小的,但是比v4中立即碰撞的机会(5.3e36中为1)的可能性更大。显然,生成v4 UUID的时间越长,冲突发生的可能性就越大,这对v1而言并非如此,但是在冲突的可能性超过节点v1实现的概率之前,您必须生成15.2亿个v4 UUID。大多数人每张桌子没有15.2亿条记录。
pgcrypto扩展名
从Postgres 9.4开始,pgcrypto模块包含gen_random_uuid()函数。此函数生成基于随机数的UUID版本4类型之一。
获取contrib模块(如果尚不可用)。
1
| sudo apt-GET install postgresql-contrib-9.4 |
使用pgcrypto模块。
1
| CREATE EXTENSION"pgcrypto"; |
gen_random_uuid()函数现在应该可用;
用法示例。
1
| INSERT INTO items VALUES( gen_random_uuid(), 54.321, 31, 'desc 1', 31.94 ) ; |
引用uuid-ossp模块上的Postgres文档。
Note: If you only need randomly-generated (version 4) UUIDs, consider using the gen_random_uuid() function from the pgcrypto module instead.
-
是的,但另请参阅blog.starkandwayne.com/2015/05/23/,他们在其中警告碎片,并建议使用uuid-ossp。
-
实际上,请参见postgresql.org/message-id/,其中揭露了Postgres中的uuid碎片问题
-
但是postgres确实在最新版本中具有聚集索引,这使得上面评论中链接的帖子没有结论性并且是错误的,我们现在回到第1篇。
-
这应该是2019年公认的答案。
-
@MichaelGoldshteyn:不,Postgres没有聚集索引(从Postgres 12开始)
1
| ALTER TABLE TABLE_NAME ALTER COLUMN id SET DEFAULT uuid_in((md5((random())::text))::cstring); |
阅读@ZuzEL的答案后,我将上面的代码用作列ID的默认值,并且工作正常。