在postgresql中从v1 uuid强制转换或提取timestamptz
我正试图从V1 uuid中提取时间戳,天真地希望它能起作用:在postgresql中从v1 uuid强制转换或提取timestamptz,postgresql,uuid,Postgresql,Uuid,我正试图从V1 uuid中提取时间戳,天真地希望它能起作用: select '3efe0a20-f1b3-11e3-bb44-14109fec739e'::uuid::timestamp; 下面是一个快速示例,演示如何提取go中的时间,但我希望postgresql具有内置功能,而不是创建一个一次性的pl/pgSql函数 我已经用数据库中的uuid测试过了,它似乎运行得很好,即使没有未签名的bigint CREATE FUNCTION uuid_timestamp(id uuid) RETURN
select '3efe0a20-f1b3-11e3-bb44-14109fec739e'::uuid::timestamp;
下面是一个快速示例,演示如何提取go中的时间,但我希望postgresql具有内置功能,而不是创建一个一次性的pl/pgSql函数 我已经用数据库中的uuid测试过了,它似乎运行得很好,即使没有未签名的bigint
CREATE FUNCTION uuid_timestamp(id uuid) RETURNS timestamptz AS $$
select TIMESTAMP WITH TIME ZONE 'epoch' +
(((('x' || lpad(split_part(id::text, '-', 1), 16, '0'))::bit(64)::bigint) +
(('x' || lpad(split_part(id::text, '-', 2), 16, '0'))::bit(64)::bigint << 32) +
((('x' || lpad(split_part(id::text, '-', 3), 16, '0'))::bit(64)::bigint&4095) << 48) - 122192928000000000) / 10000000 ) * INTERVAL '1 second';
$$ LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
这里是一个粗略的pl/pgsql实现,它将(时间戳、时钟顺序、macaddr)转换为版本1 uuid
-- Build UUIDv1 via RFC 4122.
-- clock_seq is a random 14bit unsigned int with range [0,16384)
CREATE OR REPLACE FUNCTION form_uuid_v1(ts TIMESTAMPTZ, clock_seq INTEGER, mac MACADDR)
RETURNS UUID AS $$
DECLARE
t BIT(60) := (extract(EPOCH FROM ts) * 10000000 + 122192928000000000) :: BIGINT :: BIT(60);
uuid_hi BIT(64) := substring(t FROM 29 FOR 32) || substring(t FROM 13 FOR 16) || b'0001' ||
substring(t FROM 1 FOR 12);
BEGIN
RETURN lpad(to_hex(uuid_hi :: BIGINT) :: TEXT, 16, '0') ||
(to_hex((b'10' || clock_seq :: BIT(14)) :: BIT(16) :: INTEGER)) :: TEXT ||
replace(mac :: TEXT, ':', '');
END
$$ LANGUAGE plpgsql;
-- Usage
select form_uuid_v1(now(), 666, '44:88:AA:DD:BB:88');
在我们的测试中,@Krut实现的另一种实现速度要快得多:
CREATE OR REPLACE FUNCTION uuid_timestamp(uuid UUID) RETURNS TIMESTAMPTZ AS $$
DECLARE
bytes bytea;
BEGIN
bytes := uuid_send(uuid);
RETURN to_timestamp(
(
(
(get_byte(bytes, 0)::bigint << 24) |
(get_byte(bytes, 1)::bigint << 16) |
(get_byte(bytes, 2)::bigint << 8) |
(get_byte(bytes, 3)::bigint << 0)
) + (
((get_byte(bytes, 4)::bigint << 8 |
get_byte(bytes, 5)::bigint)) << 32
) + (
(((get_byte(bytes, 6)::bigint & 15) << 8 | get_byte(bytes, 7)::bigint) & 4095) << 48
) - 122192928000000000
) / 10000 / 1000::double precision
);
END
$$ LANGUAGE plpgsql
IMMUTABLE PARALLEL SAFE
RETURNS NULL ON NULL INPUT;
此外,它假设输入是v1。如果你试图给它一个类似v4的东西,期望得到奇怪的答案,或者如果它不是v1,就修改函数来提升
=> select uuid_timestamp(uuid_generate_v4());
uuid_timestamp
----------------------------
4251-12-19 17:38:34.866+00
(1 row)
你写的东西不可能奏效。为了能够干净地提取时间组件,您需要能够以整数形式获取uuid(分成四个字,存储在bigint中,因为Pg没有无符号的int),然后对它们进行掩码和移位。PostgreSQL缺少一个很好的内置十六进制解码函数,这使得“切碎字符串和十六进制解码”的方法变得更加困难:-(@CraigRinger谢谢你的提示,你的评论“切碎字符串和十六进制解码”让我走上了正确的道路
/10000000::双精度亚秒精度
=> select uuid_timestamp(uuid_generate_v1()), now();
uuid_timestamp | now
----------------------------+-------------------------------
2020-04-29 17:40:54.519+00 | 2020-04-29 17:40:54.518204+00
(1 row)
=> select uuid_timestamp(uuid_generate_v4());
uuid_timestamp
----------------------------
4251-12-19 17:38:34.866+00
(1 row)