Postgresql 将文本表示法中的十六进制数转换为十进制数
我正在尝试使用PostgreSQL 9.1将十六进制转换为十进制 使用此查询:Postgresql 将文本表示法中的十六进制数转换为十进制数,postgresql,types,casting,hex,Postgresql,Types,Casting,Hex,我正在尝试使用PostgreSQL 9.1将十六进制转换为十进制 使用此查询: SELECT to_number('DEADBEEF', 'FMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'); 我得到以下错误: ERROR: invalid input syntax for type numeric: " " 我做错了什么?您有两个直接的问题: 不懂十六进制 X在to_number格式字符串中没有任何意义,任何没有意义的内容显然意味着“跳过字符”
SELECT to_number('DEADBEEF', 'FMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
我得到以下错误:
ERROR: invalid input syntax for type numeric: " "
我做错了什么?您有两个直接的问题:
X
在to_number
格式字符串中没有任何意义,任何没有意义的内容显然意味着“跳过字符”=> SELECT to_number('123', 'X999');
to_number
-----------
23
(1 row)
=> SELECT to_number('123', 'XX999');
to_number
-----------
3
文档中提到了双引号模式的行为:
在到日期
、到编号
和到时间戳
中,双引号字符串跳过字符串中包含的输入字符数,例如“XX”
跳过两个输入字符
但非格式化字符的非引号字符的行为似乎未指定
在任何情况下,to_number
都不是将十六进制转换为数字的正确工具,您可以这样说:
select x'deadbeef'::int;
因此,也许对您来说效果更好:
CREATE OR REPLACE FUNCTION hex_to_int(hexval varchar) RETURNS integer AS $$
DECLARE
result int;
BEGIN
EXECUTE 'SELECT x' || quote_literal(hexval) || '::int' INTO result;
RETURN result;
END;
$$ LANGUAGE plpgsql IMMUTABLE STRICT;
然后:
**为了避免整数溢出错误中出现这样的负数,请使用bigint而不是int来容纳更大的十六进制数(如IP地址)。没有动态SQL的方法
没有从表示中的十六进制数转换为数字类型,但我们可以将其用作航路点。有从位字符串(bit(n)
)到整数类型(int2
,int4
,int8
)的未记录的强制转换-内部表示是二进制兼容的
这依赖于位类型输入的一些未记录的行为
转换器,但我看不出有什么理由认为它会坏。可能是
更大的问题是它需要PG>=8.3,因为没有文本
在那之前先投点球
整数
最多8个十六进制数字
最多可将8个十六进制数字转换为位(32)
,然后强制转换为(标准4字节整数):
Postgres使用有符号整数类型,因此十六进制数在'7fffffff'
上溢出为负整数。这仍然是一种有效的、独特的表述,但含义不同。如果这很重要,请切换到bigint
;见下文
对于8个以上的十六进制数字,最不重要的字符(超出右侧)将被截断
位字符串中的4位编码1个十六进制数字。已知长度的十六进制数可以直接转换到相应的位(n)
。或者,如图所示,用前导零(0
)填充长度未知的十六进制数,并转换为位(32)
。具有7个十六进制数字和int
或8个数字和bigint
的示例:
SELECT ('x'|| 'deafbee')::bit(28)::int
, ('x'|| 'deadbeef')::bit(32)::bigint;
int4 | int8
-----------+------------
233503726 | 3735928559
bigint
最多16个十六进制数字
最多可将16个十六进制数字转换为位(64)
,然后强制转换为(int8
,8字节整数)-在上半部分再次溢出为负数:
SELECT ('x' || lpad(hex, 16, '0'))::bit(64)::bigint AS int8_val
FROM (
VALUES
('ff'::text)
, ('7fffffff')
, ('80000000')
, ('deadbeef')
, ('7fffffffffffffff')
, ('8000000000000000') -- overflow into negative number
, ('ffffffffffffffff')
, ('ffffffffffffffff123') -- too long
) t(hex);
uuid
用于最多32个十六进制数字
Postgres数据类型不是数字类型。但它是标准Postgres中最有效的类型,最多可以存储32个十六进制数字,只占用16字节的存储空间。有一个从text
到uuid
的直接强制转换(不需要位(n)
作为航路点),但只需要32个十六进制数字
SELECT lpad(hex, 32, '0')::uuid AS uuid_val
FROM (
VALUES ('ff'::text)
, ('deadbeef')
, ('ffffffffffffffff')
, ('ffffffffffffffffffffffffffffffff')
, ('ffffffffffffffffffffffffffffffff123') -- too long
) t(hex);
如您所见,标准输出是一个十六进制数字字符串,带有UUID的典型分隔符
md5散列
这对于存储md5哈希值特别有用:
SELECT md5('Store hash for long string, maybe for index?')::uuid AS md5_hash;
md5\u散列
--------------------------------------
02e10e94-e895-616e-8e23-bb7f8025da42
见:
create or replace function hex_to_bigint(hexval text) returns bigint as $$
select
(get_byte(x,0)::int8<<(7*8)) |
(get_byte(x,1)::int8<<(6*8)) |
(get_byte(x,2)::int8<<(5*8)) |
(get_byte(x,3)::int8<<(4*8)) |
(get_byte(x,4)::int8<<(3*8)) |
(get_byte(x,5)::int8<<(2*8)) |
(get_byte(x,6)::int8<<(1*8)) |
(get_byte(x,7)::int8)
from (
select decode(lpad($1, 16, '0'), 'hex') as x
) as a;
$$
language sql strict immutable;
create or replace function hex_to_int(hexval text) returns int as $$
select
(get_byte(x,0)::int<<(3*8)) |
(get_byte(x,1)::int<<(2*8)) |
(get_byte(x,2)::int<<(1*8)) |
(get_byte(x,3)::int)
from (
select decode(lpad($1, 8, '0'), 'hex') as x
) as a;
$$
language sql strict immutable;
创建或替换函数hex_to_bigint(hexval text)将bigint返回为$$
选择
(获取字节(x,0)::int8
在内部,pg bignum
使用SSL库来处理大数字。这种方法没有其他答案中提到的缺点,也没有数字。plpgsql也不会减慢它的速度。它速度快,可以处理任何大小的数字。测试用例取自Erwin的答案进行比较
CREATE EXTENSION bignum;
SELECT hex, bn_in_hex(hex::cstring)
FROM (
VALUES ('ff'::text)
, ('7fffffff')
, ('80000000')
, ('deadbeef')
, ('7fffffffffffffff')
, ('8000000000000000')
, ('ffffffffffffffff')
, ('ffffffffffffffff123')
) t(hex);
hex | bn_in_hex
---------------------+-------------------------
ff | 255
7fffffff | 2147483647
80000000 | 2147483648
deadbeef | 3735928559
7fffffffffffffff | 9223372036854775807
8000000000000000 | 9223372036854775808
ffffffffffffffff | 18446744073709551615
ffffffffffffffff123 | 75557863725914323415331
(8 rows)
您可以使用bn\u in_hex('deadbeef'):text::numeric
将类型设置为numeric,这是一个使用numeric
的版本,因此它可以处理任意大的十六进制字符串:
create function hex_to_decimal(hex_string text)
returns text
language plpgsql immutable as $pgsql$
declare
bits bit varying;
result numeric := 0;
exponent numeric := 0;
chunk_size integer := 31;
start integer;
begin
execute 'SELECT x' || quote_literal(hex_string) INTO bits;
while length(bits) > 0 loop
start := greatest(1, length(bits) - chunk_size);
result := result + (substring(bits from start for chunk_size)::bigint)::numeric * pow(2::numeric, exponent);
exponent := exponent + chunk_size;
bits := substring(bits from 1 for greatest(0, length(bits) - chunk_size));
end loop;
return trunc(result, 0);
end
$pgsql$;
例如:
=# select hex_to_decimal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
32592575621351777380295131014550050576823494298654980010178247189670100796213387298934358015
你能用这个现代答案重新评估这个问题吗?用pg bignum
Saviour!这正是我所需要的。有趣的是,遗憾的是大多数托管DBs只允许有限的已批准扩展列表。不幸的是,这个解决方案不在官方软件包中(如中所述)我发现这在可靠性、x平台可安装性等方面有点风险。它也没有很好的文档记录。我认为bignum真的属于core!哈哈,奇怪,to_number
支持最愚蠢的东西,比如罗马数字、序数后缀等等——上一次有人需要它是什么时候了。:)但没有十六进制?!
create function hex_to_decimal(hex_string text)
returns text
language plpgsql immutable as $pgsql$
declare
bits bit varying;
result numeric := 0;
exponent numeric := 0;
chunk_size integer := 31;
start integer;
begin
execute 'SELECT x' || quote_literal(hex_string) INTO bits;
while length(bits) > 0 loop
start := greatest(1, length(bits) - chunk_size);
result := result + (substring(bits from start for chunk_size)::bigint)::numeric * pow(2::numeric, exponent);
exponent := exponent + chunk_size;
bits := substring(bits from 1 for greatest(0, length(bits) - chunk_size));
end loop;
return trunc(result, 0);
end
$pgsql$;
=# select hex_to_decimal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
32592575621351777380295131014550050576823494298654980010178247189670100796213387298934358015