Tim*_*Esk 3 postgresql uuid database-design types casting
我有一个第三方应用程序连接到我的PostgreSQL数据库中的视图。它要求视图具有主键,但不能处理UUID类型(这是视图的主键)。如果将UUID用作视图中的文本,它也不能将UUID作为主键。
我想做的就是将UUID转换为数字,然后将其用作主键。然而,
SELECT x'14607158d3b14ac0b0d82a9a5a9e8f6e'::bigint
Run Code Online (Sandbox Code Playgroud)
由于数字超出范围而失败。
因此,我想使用SQL占用UUID的大部分,并创建一个int8 / bigint。我应该澄清,维持秩序是“合乎需要的”,但我知道这样做会改变某些秩序。
我试过了:
SELECT x(substring(UUID::text from 1 for 16))::bigint
Run Code Online (Sandbox Code Playgroud)
但是用于转换十六进制的x运算符似乎不喜欢方括号。我将其抽象为一个函数,但是
SELECT hex_to_int(substring(UUID::text from 1 for 16))::bigint
Run Code Online (Sandbox Code Playgroud)
仍然失败。
如何从UUID的“大头”部分得到bigint?
这都是很不稳定的,无论是问题还是您在自我解答中描述的解决方案。
首先,数据库设计和第三方应用程序之间总是不匹配,但通常表示存在更深层次的问题。为什么您的数据库首先将uuid
数据类型用作PK?与a serial
或a 相比,它们的效率不是很高bigserial
。通常,如果您在需要“保证”多个安装的唯一性的分布式环境中工作,则将使用UUID。
其次,为什么应用程序要求以PK开头(顺便说一句:视图没有PK,基础表没有)?如果仅查看数据,则PK相当无用,特别是如果它基于UUID(因此,在PK与元组的其余部分之间没有可想象的关系)。如果使用它来引用同一数据库中的其他数据,或者对现有数据进行更新或删除,则您需要确切的UUID,而不是其中的某些提取,因为数据库中的基础表或其他关系将具有准确的UUID。当然,您可以使用相同的hex_to_int()
函数转换所有UUID ,但这可以直接回到我的观点:为什么首先使用uuid
s?
第三,不要把自己几乎不了解的东西搞乱。这并非是冒犯性的,应将其作为良好的建议(在Internet上寻找那些试图通过添加自己的混淆方式来改进密码算法或随机数生成的程序员的方法;这很有趣)。uuid-ossp
软件包中提供了5种用于生成UUID的算法,尽管您知道或可以轻松找出数据库中使用的算法(uuid_generate_vX()
表定义中的函数),您知道算法的工作原理吗?UUID实际唯一性的主张是基于其128位,而不是其64位提取。您确定高64位是随机的吗?我的猜测是,完整的64位连续数比“随机性的平方根”(由于缺乏更好的方法来表述64位数字与128位数字相比周期性的理论下降)的随机性较小UUID。为什么?因为除一种算法外,所有算法均由其他非随机输入(例如网络接口的MAC地址,在生成数百万个UUID的计算机上始终相同)的随机块组成。如果64位足以实现随机值唯一性,那么a uuid
会那么长。
在您的情况下,哪种解决方案更好是很难说的,因为不清楚第三方应用程序如何处理数据库中的数据,以及该应用程序如何依赖于视图中“ PK”列的唯一性。如果应用程序在不进一步使用“ PK”的情况下简单地显示数据,则可能会起作用的一种方法是将a bigint
与uuid
(临时)表中数据库中检索到的每个内容相关联,并通过以下方式将其包含bigint
在您的视图中链接到uuid
您(临时)表中的。由于您无法触发on 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;
Run Code Online (Sandbox Code Playgroud)
不漂亮,没有效率,但是很简单。
您可以在没有动态SQL的情况下更快地执行此操作。请先阅读以下内容:
在您的情况下,请uuid
以文本表示形式获取列的前16个十六进制数字。(对于实际uuid
数据类型作为输入,转换为text
与:uuid::text
)的Postgres接受各种格式进行输入。您给定的字符串文字是其中之一:
14607158d3b14ac0b0d82a9a5a9e8f6e
Run Code Online (Sandbox Code Playgroud)
但是,UUID的默认文本表示形式(以及text
Postgres中数据类型的输出uuid
)在预定义的位置添加了连字符:
14607158-d3b1-4ac0-b0d8-2a9a5a9e8f6e
Run Code Online (Sandbox Code Playgroud)
UUID是由小写的十六进制数字序列组成的,在由连字符分隔的几组中,特别是一组8位数字,然后是三组4位数字,然后是一组12位数字,总共32位数字代表128位。
因此表达式必须类似于:
SELECT ('x' || translate(left(uuid::text, 18), '-', ''))::bit(64)::bigint;
Run Code Online (Sandbox Code Playgroud)
采取的第一个18个字符以补偿两个包括连字符和钢带的那些,最廉价的用translate()
。
或者,如果您将字符串类型作为输入操作并且格式可能有所不同,请首先去除连字符以确保:
SELECT ('x' || left(translate(uuid_as_string, '-', ''), 16))::bit(64)::bigint;
Run Code Online (Sandbox Code Playgroud)
只是稍微贵一点。
请注意,Postgres使用带符号的整数,因此bigint
上半部溢出到负数-与您的目的无关。
如果有可能bigserial
,请在基础表中添加一列,然后使用该列。