在PostgreSQL中从uuid的大端创建一个大整数

时间:2014-12-04 01:48:39

标签: postgresql database-design types casting uuid

我有一个第三方应用程序连接到PostgreSQL数据库中的视图。它要求视图具有主键但不能处理UUID类型(这是视图的主键)。如果UUID从视图中作为文本提供,它也无法将UUID作为主键处理。

我想要做的是将UUID转换为数字并将其用作主键。然而,

SELECT x'14607158d3b14ac0b0d82a9a5a9e8f6e'::bigint

失败,因为号码超出范围。

所以相反,我想使用SQL来获取UUID的大头并创建一个int8 / bigint。我应该澄清,保持秩序是“可取的”,但我知道有些订单会因此而改变。

我试过了:

SELECT x(substring(UUID::text from 1 for 16))::bigint

但是用于转换hex的x运算符似乎不像括号。我把它抽象成一个函数,但是

SELECT hex_to_int(substring(UUID::text from 1 for 16))::bigint

仍然失败。

如何从UUID的“大头”一半获得一个bigint?

4 个答案:

答案 0 :(得分:5)

如果没有动态SQL,您可以更快地完成此操作。 首先阅读:

在您的情况下,请在文本表示中取uuid列的前16位十六进制数字。 (对于实际的uuid数据类型作为输入,使用:text强制转换为uuid::text)Postgres接受各种格式的输入。您给定的字符串文字就是其中之一:

14607158d3b14ac0b0d82a9a5a9e8f6e

但是UUID的默认文本表示(以及Postgres中text输出的数据类型uuid)在预定义的位置添加了连字符:

14607158-d3b1-4ac0-b0d8-2a9a5a9e8f6e

The manual:

  

UUID被写为一系列小写的十六进制数字   由连字符分隔的几个组,特别是一组8位数   接着是三组4位数后跟一组12位数,   总共32位代表128位。

所以表达式必须是:

SELECT ('x' || translate(left(uuid::text, 18), '-', ''))::bit(64)::bigint;

取第一个 18 字符来补偿两个包含的连字符并删除那些,最便宜的translate()

或者,如果您使用字符串类型作为输入操作并且格式可能不同,请首先删除连字符以确保:

SELECT ('x' || left(translate(uuid_as_string, '-', ''), 16))::bit(64)::bigint;

只是稍贵一点。

SQL Fiddle.

请注意,Postgres使用签名的整数,因此bigint溢出到上半部分的负数 - 这与您的目的无关。

数据库设计

如果可能的话,在基础表中添加bigserial列并改为使用它。

答案 1 :(得分:4)

这一切都非常不稳定,无论是你在自我回答中描述的问题还是解决方案。

首先,数据库设计和第三方应用程序之间的不匹配始终是可能的,但通常表示更深层次的问题。为什么您的数据库首先使用uuid数据类型作为PK?与serialbigserial相比,它们效率不高。通常,如果您在分布式环境中工作,则需要使用UUID,并且需要保证"多个安装的独特性。

其次,为什么应用程序需要PK开始(顺便说一句:视图没有PK,底层表格如何)?如果只是查看数据那么PK是相当无用的,特别是如果它基于UUID(并且因此在PK和元组的其余部分之间没有可想到的关系)。如果它用于引用同一数据库中的其他数据或更新或删除现有数据,那么您需要准确的UUID而不是它的一些摘录,因为数据库中的基础表或其他关系将具有确切的UUID。当然,您可以使用相同的hex_to_int()函数转换所有UUID,但这会直接回到我的观点:为什么首先使用uuid

第三,不要搞乱你很少或根本不知道的事情。这并不是冒犯性的,把它作为善意的建议(在互联网上环顾四周试图改进加密算法或随机数生成的程序员,通过添加他们自己的混淆代码;非常有趣的读取)。在uuid-ossp包中有5种用于生成UUID的算法,虽然您知道或者很容易找出数据库中使用的算法(表定义中的uuid_generate_vX()函数),但很可能),你知道算法是如何工作的吗? UUID的实际唯一性的主张基于其128位,而不是64位的提取。你确定高64位是随机的吗?我的猜测是64个连续位的随机性小于随机性的平方根" (因为没有更好的方法来表示完整UUID的64位数字与128位数字的周期性的理论下降)。为什么?因为除了一个算法之外的所有算法都由随机的非随机输入块组成(例如网络接口的MAC地址,在生成数百万个UUID的机器上总是相同的)。如果64位足以实现随机值唯一性,则uuid就足够了。

在您的情况下,更好的解决方案很难说,因为不清楚第三方应用程序对数据库中的数据做了什么以及它对于" PK&#的唯一性有多依赖34;视图中的列。如果应用程序在不进一步使用" PK"而不是简单地显示数据的情况下,该方法可能会起作用。将bigint与数据库中每个检索到的uuid关联到一个(临时)表中,并通过链接到您bigint中的uuidSELECT包含在您的视图中(临时)表。由于您无法触发bigint语句,因此您需要一个函数来为应用程序检索的每个uuid生成uuid。在对视图的基础表进行更新或删除时,或者从相关表中选择数据时,您将查找与应用程序中传入的bigint对应的CREATE TEMPORARY TABLE temp_table( tempint bigserial PRIMARY KEY, internal_uuid uuid); CREATE INDEX ON temp_table(internal_uuid); CREATE FUNCTION temp_int_for_uuid(pk uuid) RETURNS bigint AS $$ DECLARE id bigint; BEGIN SELECT tempint INTO id FROM temp_table WHERE internal_uuid = pk; IF NOT FOUND THEN INSERT INTO temp_table(internal_uuid) VALUES (pk) RETURNING tempint INTO id; END IF; RETURN id; END; $$ LANGUAGE plpgsql STRICT; 。查找表和函数看起来有点像这样:

{{1}}

不漂亮,不高效,但是万无一失。

答案 2 :(得分:1)

找到解决方案。

UUID::text将返回带连字符的字符串。为了使substring(UUID::text from 1 for 16)创建一个x可以解析为十六进制的字符串,需要先删除连字符。

最终查询如下:

SELECT hex_to_int(substring((select replace(id::text,'-','')) from 1 for 16))::bigint FROM table

hext_to_int功能需要能够处理bigint,而不仅仅是int。它看起来像:

CREATE OR REPLACE FUNCTION hex_to_int(hexval character varying)
  RETURNS bigint AS
$BODY$
DECLARE
   result  bigint;
BEGIN
 EXECUTE 'SELECT x''' || hexval || '''::bigint' INTO result;
 RETURN result;
END;
$BODY$`

答案 3 :(得分:1)

使用bit()函数解析从UUID的substr构建的十六进制文字中的十进制数:

select ('x'||substr(UUID, 1, 16))::bit(64)::bigint

请参阅SQLFiddle