【问题标题】:Generating a UUID in Postgres for Insert statement?在 Postgres 中为插入语句生成 UUID?
【发布时间】:2012-09-12 09:05:14
【问题描述】:

我的问题很简单。我知道 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。

【问题讨论】:

  • Postgres 原生支持 UUID 作为数据类型,甚至可以被索引并用作主键。但是要生成一个 UUID 值,例如为一个列建立一个默认值,你需要一个 Postgres 扩展(一个插件)。 Postgres 的许多构建(发行版)都包含这样的扩展,但不激活该扩展。请参阅correct answer by Craig Ringer 了解如何激活它。
  • 如果您安装了 uuid-ossp 并且仍然收到此错误,请尝试在函数前面加上您的模式名称,例如select dbo.uuid_generate_v4()

标签: postgresql uuid postgresql-8.4


【解决方案1】:

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

对于现代 PostgreSQL 版本(9.1 和更高版本),这很容易:

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

但对于 9.0 及以下版本,您必须改为运行 SQL 脚本来加载扩展。见the documentation for contrib modules in 8.4

对于 Pg 9.1 和更新版本,请改为阅读 the current contrib docsCREATE EXTENSION。这些功能在 9.0 或更早的版本中不存在,例如您的 8.4。

如果您使用的是 PostgreSQL 的打包版本,您可能需要安装一个单独的包,其中包含 contrib 模块和扩展。在您的包管理器数据库中搜索“postgres”和“contrib”。

【讨论】:

  • @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。非常感谢!
  • 注意,如果你不安装postgresql-contrib包,你会得到错误:ERROR: could not open extension control file "/usr/share/postgresql/9.3/ extension/uuid-ossp.control": 没有这样的文件或目录
  • 当错误字符串在 Google 上被删除时,我发布了该评论。它还给出了一个特定的包名称,至少对于 Ubuntu。
  • 如果您导入了一个已经在 Extensions 中包含 uuid-ossp 的数据库,uuid_generate_v4() 可能无法正常工作。如果是这种情况,只需删除扩展程序,然后重新创建它,它应该可以工作。
【解决方案2】:

没有扩展(作弊)

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

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

(至少在 8.4 中有效)

  • 感谢@Erwin Brandstetter 对clock_timestamp() 的解释。

如果您需要有效的 v4 UUID

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

【讨论】:

  • 跟进你的 PS:SELECT uuid_in(md5(random()::text || now()::text)::cstring);
  • @MattDiPasquale 可能没有任何意义上比使用uuid-ossp“更好”,但我正在处理一个 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 Göncüoğlu:生成有效的 v4 UUID 相当简单(不过,字符串叠加方法浪费了 2 位随机性):select overlay(overlay(md5(random()::text || ':' || clock_timestamp()::text) placing '4' from 13) placing '8' from 17)::uuid;
【解决方案3】:

answer by Craig Ringer 是正确的。这是 Postgres 9.1 及更高版本的更多信息……

扩展可用吗?

您只能安装已经为您的 Postgres 安装构建的扩展(您的 集群 在 Postgres 术语中)。例如,我发现 uuid-ossp 扩展名包含在 Mac OS X 安装程序的一部分中,来自 EnterpriseDB.com 的 providedfew dozen extensions 中的任何一个都可能可用。

要查看 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 团队为该命令选择了一个奇怪的名称。在我看来,他们应该选择“安装扩展”或“使用扩展”之类的东西。

验证安装

您可以通过运行此 SQL 查询 pg_extension 系统目录来验证扩展是否已成功安装在所需的数据库中:

SELECT * FROM pg_extension;

UUID 作为默认值

有关更多信息,请参阅问题:Default value for UUID column in Postgres

老路

以上信息使用 Postgres 9.1 的新 Extensions 功能 added。在以前的版本中,我们必须在 .sql 文件中查找并运行脚本。添加了扩展功能以使安装更容易,为creator of an extension 做更多的工作以减少扩展的用户/消费者的工作。更多讨论请见我的blog post

UUID 的类型

顺便提一下,问题中的代码调用了函数uuid_generate_v4()。这会生成一种称为Version 4 的类型,其中几乎所有的 128 位都是随机生成的。虽然这适用于较小的行集的有限使用,但如果您想几乎消除任何冲突的可能性,请使用 UUID 的另一个“版本”。

例如,原来的Version 1将主机的MAC address与当前日期时间和任意数字组合在一起,碰撞的可能性几乎为零。

如需更多讨论,请参阅相关问题的my Answer

【讨论】:

  • 如果您不确定并且不想检查(例如在脚本中),也可以使用CREATE EXTENSION IF NOT EXISTS ...
  • 第 4 版 UUID 几乎适用于任何大小的数据集,而不仅仅是“在较小的行集上有限使用”。您必须在大约 85 年内每秒生成 10 亿个 UUID(或大约 4500 万 TB 的数据,比当今最大的数据库大数千倍)才能有 50% 的碰撞机会。除非您是 NSA,否则版本 4 几乎适用于任何目的。另一方面,版本 1 的缺点是 MAC 地址是按顺序分配的(并且经常被欺骗或不可用),这也是引入更高版本的部分原因。
  • @BasilBourque v1 的问题不是正确实施时发生冲突的可能性,而是错误实施的可能性。正如维基百科所说:“第 1 版和第 2 版 UUID 的唯一性……还取决于网卡制造商正确地为其卡分配唯一的 MAC 地址,这与其他制造过程一样容易出错。”此外,在某些容器化或虚拟化环境中,来自底层硬件的真实 MAC 地址不可用。如果许多容器具有相同的 MAC 但有自己的 clockseq 计数器,它们的 v1 UUID 可能会发生冲突。
  • @BasilBourque v1 中的弱点不是我评论的重点。您的原始答案暗示 v4 不适合大型数据集,因为冲突概率高于 v1。这是误导性的,并且可能是错误的,尽管很难计算 v1 的冲突概率,因为它是如此依赖于实现。
  • @BasilBourque 例如,node-uuid 项目在 4.6e18 中计算它们的 clockseq 计数器相同(以便两个进程将生成相同的 v1 UUID 序列)的概率为 1。这是很小的,是的,但比 v4 中立即碰撞的可能性要大得多,在 5.3e36 中是 1。显然,您生成 v4 UUID 的时间越长,发生冲突的可能性就越大,而 v1 并非如此,但您必须生成 15.2 亿个 v4 UUID,然后冲突概率超过节点的 v1 实现。大多数人每张表没有 15.2 亿条记录。
【解决方案4】:

pgcrypto分机

从 Postgres 9.4 开始,pgcrypto 模块包括 gen_random_uuid() 函数。此函数生成一个基于随机数的Version 4 type of 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 doc on uuid-ossp 模块。

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

【讨论】:

  • 是的,但另请参阅blog.starkandwayne.com/2015/05/23/…,他们警告碎片并建议使用 uuid-ossp。
  • 其实看postgresql.org/message-id/… Postgres中uuid碎片问题的揭秘
  • 但是 postgres 在最新版本中确实有聚集索引,使得上述评论中链接的帖子不确定且不正确,我们马上回到第 1 格。
  • @MichaelGoldshteyn:不,Postgres 没有有聚集索引(从 Postgres 12 开始)
  • 第一条评论中的文章已移至here。但是,TLDR,关于揭穿“碎片化”问题的第二个链接找到了开始神话的文章,它基于具有聚集索引的数据库,正如@a_horse_with_no_name 所说,postgres 没有。它具有对数据进行一次性重新排序的功能,因此键是集群的,但它与 SQL Server 不同。 SQL Server 可以存储按键排序的行,并在添加新记录时随机排列以保持键的顺序。
【解决方案5】:

PostgreSQL 13 原生支持 gen_random_uuid ():

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

gen_random_uuid () → uuid

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

db<>fiddle demo

【讨论】:

  • 我不明白为什么还有这么多其他(无效)答案,就这么简单!应该是公认的答案
  • @pdem 因为 PostgreSQL 13 相当新。例如,我仍在开发服务器版本 10。不过,这似乎是版本 13 的最佳替代方案。
  • @borellini 我明白了,我实际上在 12 年有一个生产服务器,我必须创建这个补丁功能才能让它工作create function gen_random_uuid() RETURNS uuid as $$ SELECT md5(random()::text || clock_timestamp()::text)::uuid $$ LANGUAGE SQL;
【解决方案6】:

从 2021 年开始更新, 无需花哨的技巧即可在 insert 语句上自动生成 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 ()功能不满意,可以开启。大多数时候,没有它们你应该没问题

【讨论】:

【解决方案7】:
ALTER TABLE table_name ALTER COLUMN id SET DEFAULT uuid_in((md5((random())::text))::cstring);

在阅读@ZuzEL 的回答后,我使用上面的代码作为列 id 的默认值,它工作正常。

【讨论】:

    【解决方案8】:

    uuid-ossp 模块提供生成通用唯一标识符 (UUID) 的功能

    uuid_generate_v1() 此函数生成版本 1 UUID。

    1. 添加扩展

    如果“uuid-ossp”不存在则创建扩展;

    1. 验证扩展

    从 pg_extension 中选择 *;

    1. 运行查询

    INSERT INTO table_name(id, column1, column2 , column3, ...) 值 (uuid_generate_v1(), value1, value2, value3...);

    验证表数据

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-29
      • 2017-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多