在Postgres中生成用于INSERT语句的UUID?

630

我的问题很简单。我知道UUID的概念,并希望生成一个UUID来指代我的数据库中店铺中的每个“项目”。听起来很合理吧?

问题是以下这行代码会返回错误:

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。


18
Postgres原生支持UUID作为数据类型,甚至可以作为索引和主键使用。但是要生成UUID值,例如为列建立默认值,您需要一个Postgres扩展(插件)。许多Postgres发布版本包括这样的扩展,但不会激活该扩展。请参见Craig Ringer的正确答案,了解如何激活它。 - Basil Bourque
4
如果您已经安装了uuid-ossp并且仍然遇到此错误,请尝试在函数前加上模式名称,例如:select dbo.uuid_generate_v4() - Richard
如果您想让Postgres为PK(主键)之外的列生成UUID,我终于找到了一种特定的方法:https://dev59.com/HvTvpIgB1922wOYJZ-k9#75900798 - chaotic3quilibrium
2
如果您使用的是Postgres 13+版本,无需安装扩展,只需调用gen_random_uuid()函数即可。
来源:https://dev59.com/a2cs5IYBdhLWcg3w433H#61970869
- Jahan Zinedine
12个回答

665

uuid-ossp是一个扩展模块,因此默认情况下不会加载到服务器中。您必须将其加载到您的数据库中才能使用它。

对于现代的PostgreSQL版本(9.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”。


7
您正在使用发行版打包的PostgreSQL,因此您应该能够直接使用apt-get install postgresql-contrib或类似命令进行安装。尝试使用apt-cache search postgresql |grep contrib查找您想要的软件包名称。 - Craig Ringer
2
sudo apt-get install postgresql-contrib 已成功运行。然后我必须运行 psql -d dbname -f SHAREDIR/contrib/module.sql,现在它可以工作了!现在 select uuid_generate_v1(); 返回 1。非常感谢! - fIwJlxSzApHEZIl
6
请注意,如果您没有安装 postgresql-contrib 软件包,将会出现以下错误: ERROR: could not open extension control file "/usr/share/postgresql/9.3/extension/uuid-ossp.control": No such file or directory - Drew Noakes
2
如果您导入的数据库已经在扩展中包含了uuid-ossp,则uuid_generate_v4()可能无法工作。如果是这种情况,请删除该扩展,并重新创建它,应该就可以正常使用了。 - Dragos Rusu
2
在任何SQL脚本中使用IF NOT EXISTS是一个好习惯,特别是当你重新运行脚本或将其转移到已经加载了该脚本的另一个系统时,它可以防止出现错误。 (我刚刚将其添加到答案中。) - Randall
显示剩余6条评论

505

不使用扩展(作弊)

如果你需要一个有效的v4 UUID

SELECT uuid_in(overlay(overlay(md5(random()::text || ':' || random()::text) placing '4' from 13) placing to_hex(floor(random()*(11-8+1) + 8)::int)::text from 17)::cstring);

输入图像描述

  • 感谢@Denis Stafichuk@Karsten@autronix

或者,您可以通过以下方式简单获取类似UUID的值(如果您不关心其有效性):

SELECT uuid_in(md5(random()::text || random()::text)::cstring);

output>> c2d29867-3d0b-d497-9191-18a9d8ee7830

(至少在8.4版本中可用)


7
针对您的附言进行跟进:SELECT uuid_in(md5(random()::text || now()::text)::cstring); (注:这是一个SQL查询语句,用于生成随机UUID) - Blaskovicz
6
可能不会比使用 uuid-ossp 更好,但是例如我正在处理一个 PostgreSQL 实例,我没有足够的权限安装扩展。 - Stefan Haberl
29
@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-无需更多魔法。使用案例:https://dev59.com/cmsy5IYBdhLWcg3wyxCi#8335376 - Erwin Brandstetter
30
不行,如果这确实起作用的话,那只是纯粹的运气。UUID有一定的格式,不是仅仅将随机十六进制字符组合在一起。例如,第3组的第一个数字是UUID版本(现在通常为4)。如果您的应用程序检查该数字以查看正在处理哪个版本的UUID,并相应地执行某些操作,则会导致代码失败。 - Tuncay Göncüoğlu
9
很容易生成有效的v4 UUID(字符串覆盖方法浪费了2位随机性):select overlay(overlay(md5(random()::text || ':' || clock_timestamp()::text) placing '4' from 13) placing '8' from 17)::uuid; - Karsten
显示剩余10条评论

278

PostgreSQL 13原生支持 gen_random_uuid()

PostgreSQL包含了一个用于生成UUID的函数:

gen_random_uuid () → uuid

此函数返回版本4(随机)UUID。这是最常用的UUID类型,并适用于大多数应用程序。

db<>fiddle演示


11
我不明白为什么会有那么多其他的(无法工作的)答案,事情就这么简单!应该接受这个答案。 - pdem
10
@pdem 由于PostgreSQL 13相对较新,例如我仍在使用服务器版本10。尽管如此,这似乎是版本13的最佳替代方案。 - borellini
5
我明白了,实际上我在12中有一个生产服务器,我不得不创建这个补丁函数才能使其工作。create function gen_random_uuid() RETURNS uuid as $$ SELECT md5(random()::text || clock_timestamp()::text)::uuid $$ LANGUAGE SQL; - pdem
1
我喜欢这个 Polyfill,很容易升级。 - Ben Longo
它生成哪个版本的UUID? - TheRealChx101
1
@TheRealChx101 "这个函数返回一个版本4(随机)UUID。" - Lukasz Szozda

109

Craig Ringer的回答是正确的。以下是有关Postgres 9.1及更高版本的一些详细信息...

扩展是否可用?

只有在为您的Postgres安装(Postgres术语中的“集群”)构建了扩展名时,才能安装扩展名。例如,我在Mac OS X上找到了uuid-ossp扩展名,它是由EnterpriseDB.com友好提供的安装程序的一部分。可以使用几十个扩展名之一。

要查看uuid-ossp扩展名是否在您的Postgres集群中可用,请运行此SQL来查询pg_available_extensions系统目录:

SELECT * FROM pg_available_extensions;

安装扩展

要安装与UUID相关的扩展,请使用以下SQL中所示的CREATE EXTENSION命令:

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

注意:尽管文档说明相反,但我发现在扩展名周围使用引号是必需的。

SQL标准委员会或Postgres团队选择了一个奇怪的命令名称。在我看来,他们应该选择类似于“INSTALL EXTENSION”或“USE EXTENSION”之类的名称。

验证安装

您可以运行以下SQL查询pg_extension系统目录来验证扩展已成功安装到所需数据库中:

SELECT * FROM pg_extension;

UUID作为默认值

更多信息请查看问题:Postgres中UUID列的默认值

旧方法

上面的信息使用了新的扩展功能 添加 到Postgres 9.1. 在以前的版本中,我们必须找到并运行.sql文件中的脚本。添加扩展功能使安装更加容易,为扩展程序的创建者提供了更少的工作量,同时也减轻了用户/消费者的工作量。有关更多讨论,请参见我的博客文章

UUID的类型

顺便提一下,问题中的代码调用了函数uuid_generate_v4()。这会生成一个称为Version 4的类型,其中几乎所有的128位都是随机生成的。虽然对于较小的行集合有限制使用是可以的,但如果您想消除任何可能的冲突,就需要使用另一种UUID的“版本”。
例如,原始的Version 1将主机计算机的MAC地址与当前日期时间和一个任意数字相结合,碰撞的可能性几乎为零。
有关更多讨论,请参见相关问题上我的答案

1
如果您不确定并且不想在脚本中检查的话,您也可以使用CREATE EXTENSION IF NOT EXISTS ... - Uwe Allner
5
版本4的UUID适用于几乎任何大小的数据集,不仅限于“在较小的行集上使用”。你必须每秒生成10亿个UUID约85年(或者大约4500万TB的数据,比今天最大的数据库还要大数千倍)才能有50%的冲突几率。除非你是美国国家安全局,否则版本4适用于任何目的。相反,版本1受制于MAC地址是顺序分配的事实(并且经常被欺骗或无法使用),这也是后来推出新版本的原因之一。 - Jazz
2
@BasilBourque v1的问题不在于正确实现时碰撞的概率,而在于错误实现的概率。正如维基百科所述:“版本1和2 UUID的唯一性...还取决于网络卡制造商正确分配唯一的MAC地址给它们的卡,这与其他制造过程一样容易出错。”此外,在某些容器化或虚拟化环境中,来自底层硬件的真实MAC地址是不可用的。如果许多容器具有相同的MAC但具有自己的clockseq计数器,则它们的v1 UUID可能会发生冲突。 - Jazz
2
@BasilBourque,虽然v1的弱点不是我评论的重点。但是你原来的回答暗示v4由于碰撞概率比v1高而不适用于大型数据集。这是误导性的,可能是错误的,尽管很难计算v1的碰撞概率,因为它非常依赖于实现。 - Jazz
2
例如,node-uuid项目计算其clockseq计数器相同的概率(使得两个进程生成相同的v1 UUID序列)为4.6e18中的1。这很小,但比v4中立即发生碰撞的机会1 in 5.3e36要高得多。显然,您生成的v4 UUID越长,发生冲突的可能性就越大,而这对于v1不是真的,但是在超过节点v1实现的冲突概率之前,您必须生成15.2亿个v4 UUID。大多数人每个表都没有15.2亿条记录。 - Jazz
显示剩余3条评论

86

pgcrypto扩展

从Postgres 9.4开始,pgcrypto模块包括gen_random_uuid()函数。此函数生成基于随机数字的版本4 UUID之一。

如果未安装,请获取contrib模块。

sudo apt-get install postgresql-contrib-9.4

使用 pgcrypto 模块。

CREATE EXTENSION "pgcrypto";
gen_random_uuid()函数现在应该可用;
例如用法。
INSERT INTO items VALUES( gen_random_uuid(), 54.321, 31, 'desc 1', 31.94 ) ;


Postgres关于uuid-ossp模块的文档中引用。

注意:如果您只需要随机生成(版本4)UUID,请考虑改用来自pgcrypto模块的gen_random_uuid()函数。


3
是的,但请参考https://blog.starkandwayne.com/2015/05/23/uuid-primary-keys-in-postgresql/,他们警告关于碎片化,并建议使用uuid-ossp。 - Malik A. Rumi
4
实际上,请查看 https://www.postgresql.org/message-id/20151222124018.bee10b60b3d9b58d7b3a1839%40potentialtech.com ,其中反驳了Postgres中uuid碎片化问题的说法。 - Bob Kocisko
但是在最新版本中,Postgres确实具有聚集索引,这使得上面评论中链接的文章不确定和不正确,我们又回到了起点。 - Michael Goldshteyn
2
@MichaelGoldshteyn:不,Postgres没有聚集索引(截至Postgres 12) - user330315
第一条评论中的文章已经移动到这里。简而言之,关于“碎片化”问题的第二个链接揭穿了这个神话的起源文章,它基于具有聚集索引的数据库,正如@a_horse_with_no_name所说,Postgres没有这样的索引。它有一个函数可以对数据进行一次重新排序,使键聚集在一起,但这与SQL Server不同。SQL Server可以按键存储行,并在添加新记录时重新排列以保持键的顺序。 - ps2goat

70

2021年更新:

在插入语句中自动生成uuid无需花哨的技巧。

只需要做一件事:

  1. DEFAULT gen_random_uuid()设置为您的uuid列的默认值。就这样。

比如,您有一个像这样的表:

CREATE TABLE table_name (
    unique_id UUID DEFAULT gen_random_uuid (),
    first_name VARCHAR NOT NULL,
    last_name VARCHAR NOT NULL,
    email VARCHAR NOT NULL,
    phone VARCHAR,
    PRIMARY KEY (unique_id)
);

现在你无需做任何事情来自动插入uuid值到unique_id列中。因为你已经为它定义了默认值,你只需要简单地专注于插入其他列,并且postgresql会处理你的unique_id。以下是一个示例插入语句:

INSERT INTO table_name (first_name, last_name, email, phone) 
VALUES (
    'Beki',
    'Otaev',
    'beki@bekhruz.com',
    '123-456-123'
)

请注意,不需要向unique_id插入内容,因为它已经得到了处理。

关于其他扩展,比如uuid-ossp,如果您对Postgres的标准gen_random_uuid()函数不满意,您可以使用它们。大多数情况下,你应该可以在没有它们的情况下正常工作。


2
正如Lukasz Szozda在https://dev59.com/a2cs5IYBdhLWcg3w433H#61970869中指出的那样,`gen_random_uuid()`已经作为PostgreSQL 13的核心部分可用(因此不需要安装任何扩展程序)。 - Vainstein K
在这篇文章的底部:https://www.starkandwayne.com/blog/uuid-primary-keys-in-postgresql/,讨论了 gen_random_uuid() 的一个可能缺点。对于该陈述的有效性有任何评论吗? - Jacolack
1
如果你指的是“与键空间碎片化相关的负面影响”,那么不,这不是一个问题:https://www.postgresql.org/message-id/20151222124018.bee10b60b3d9b58d7b3a1839%40potentialtech.com
  • 正如在brillout的回答评论中提到的。
- ctholho

6
Postgres v13+
INSERT INTO your_table
VALUES (gen_random_uuid(), 'value column 2', 'value column 3')

5
uuid-ossp 模块提供了生成通用唯一标识符(UUID)的函数。 uuid_generate_v1() 函数生成版本 1 UUID。
1. 添加扩展
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
2. 验证扩展
SELECT * FROM pg_extension;
3. 运行查询
INSERT INTO table_name(id, column1, column2 , column3, ...) VALUES (uuid_generate_v1(), value1, value2, value3...);
验证表数据。

4
ALTER TABLE table_name ALTER COLUMN id SET DEFAULT uuid_in((md5((random())::text))::cstring);

在阅读@ZuzEL的答案后,我使用上述代码作为列id的默认值,它工作得很好。


2
对于小于13版本的Postgres:
CREATE EXTENSION IF NOT EXISTS "pgcrypto";

-- gives:

SELECT gen_random_uuid();

在PostgreSQL版本13+上:
SELECT gen_random_uuid();

在这种情况下,当你不知道Postgres的版本,但假设至少为9,并且对于你来说不要去无谓地启用此扩展很重要。
DO
$do$
BEGIN
  IF current_setting('server_version_num')::integer < 130000 THEN
    CREATE EXTENSION IF NOT EXISTS "pgcrypto";
  END IF;
END
$do$

SELECT gen_random_uuid();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接