我有一个第三方应用程序连接到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?
答案 0 :(得分:5)
如果没有动态SQL,您可以更快地完成此操作。 首先阅读:
在您的情况下,请在文本表示中取uuid
列的前16位十六进制数字。 (对于实际的uuid
数据类型作为输入,使用:text
强制转换为uuid::text
)Postgres接受各种格式的输入。您给定的字符串文字就是其中之一:
14607158d3b14ac0b0d82a9a5a9e8f6e
但是UUID的默认文本表示(以及Postgres中text
输出的数据类型uuid
)在预定义的位置添加了连字符:
14607158-d3b1-4ac0-b0d8-2a9a5a9e8f6e
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;
只是稍贵一点。
请注意,Postgres使用签名的整数,因此bigint
溢出到上半部分的负数 - 这与您的目的无关。
如果可能的话,在基础表中添加bigserial
列并改为使用它。
答案 1 :(得分:4)
这一切都非常不稳定,无论是你在自我回答中描述的问题还是解决方案。
首先,数据库设计和第三方应用程序之间的不匹配始终是可能的,但通常表示更深层次的问题。为什么您的数据库首先使用uuid
数据类型作为PK?与serial
或bigserial
相比,它们效率不高。通常,如果您在分布式环境中工作,则需要使用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
中的uuid
将SELECT
包含在您的视图中(临时)表。由于您无法触发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