将hex文本表示转换为十进制数
我想使用PostgreSQL 9.1将hex转换为十进制
与此查询:
SELECT to_number('DEADBEEF', 'FMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
我得到以下错误:
ERROR: invalid input syntax for type numeric: " "
我究竟做错了什么?
你有两个直接的问题:
-
to_number
不理解hex。 -
X
在to_number
格式string中没有任何意义,任何没有含义的东西显然意味着“跳过字符”。
(2)我没有权威的理由,只是经validation据:
=> SELECT to_number('123', 'X999'); to_number ----------- 23 (1 row) => SELECT to_number('123', 'XX999'); to_number ----------- 3
文档提到了双引号模式应该如何performance:
在
to_date
,to_number
和to_timestamp
,双引号string会跳过string中包含的input字符数,例如"XX"
跳过两个input字符。
但非格式化字符的非引号字符的行为似乎是未指定的。
在任何情况下, to_number
不是hex转换为数字的正确工具,你想要这样说:
select x'deadbeef'::int;
所以这个函数可能对你更好:
CREATE OR REPLACE FUNCTION hex_to_int(hexval varchar) RETURNS integer AS $$ DECLARE result int; BEGIN EXECUTE 'SELECT x''' || hexval || '''::int' INTO result; RETURN result; END; $$ LANGUAGE plpgsql IMMUTABLE STRICT;
然后:
=> select hex_to_int('DEADBEEF'); hex_to_int ------------ -559038737 ** (1 row)
**要避免整数溢出错误这样的负数,请使用bigint而不是int来容纳较大的hex数(如IP地址)。
有没有dynamicSQL的方法 。
最大。 8个hex数字
在text
表示中不会有hex数字转换为数字types,但是我们可以使用bit(n)
作为航点。 位串中的4位编码1个hex数字。 从位串bit(32)
(最多8个hex数字)到integer
(标准的4字节整数)有一个未公开的转换 – 内部表示是二进制兼容的。
SELECT ('x' || lpad(hex, 8, '0'))::bit(32)::int AS int_val FROM ( VALUES ('1'::text) ,('f') ,('100') ,('7fffffff') ,('80000000') ,('deadbeef') ,('ffffffff') ) AS t(hex);
结果:
int_val ------------ 1 15 256 2147483647 -2147483648 -559038737 -1
4个字节足以将所有hex数字编码为8位数字,但Postgres中的integer
是带符号的types,所以在'7fffffff'
之上'7fffffff'
hex数字溢出到一个负的int数字中。 这仍然是一个独特的代表,但意义是不同的。 如果这个问题切换到bigint
,请看下面。
对于长度未知的hex数字,我们需要填充前导零,如下所示,将其转换为bit(32)
。 对于已知长度的数字,我们可以调整长度说明符。 具有7个hex数字和int
或8个数字和bigint
示例:
SELECT ('x'|| 'deafbee')::bit(28)::int , ('x'|| 'deadbeef')::bit(32)::bigint; int4 | int8 -----------+------------ 233503726 | 3735928559
最大。 hex数字
使用bigint
( int8
字节整数)最多16个hex数字 – 溢出到上半部分的负数:
SELECT ('x' || lpad(hex, 16, '0'))::bit(64)::bigint AS int8_val FROM ( VALUES ('ff'::text) , ('7fffffff') , ('80000000') , ('deadbeef') , ('7fffffffffffffff') , ('8000000000000000') , ('ffffffffffffffff') , ('ffffffffffffffff123') -- too long ) t(hex);
结果:
int8_val --------------------- 255 2147483647 2147483648 3735928559 9223372036854775807 -9223372036854775808 -1 -1
对于16位以上的hex数字,最不重要的字符(超出右边)被截断 。
这个angular色依赖于无证的行为 ,我在这里引用汤姆·莱恩的话 :
这是依赖于位型input转换器的一些无法certificate的行为,但我没有理由期望会中断。 一个可能更大的问题是,它需要PG> = 8.3,因为在此之前没有要投射的文本。
用于最大的UUID 32位hex数字
Postgres的uuid
数据types不是一个数字types ,所以这偏离了问题。 但它是标准Postgres中最高效的types,最多可以存储32位hex数字,只占用16个字节的存储空间。 有一个直接强制转换 ,但正好需要 32位hex数字。
SELECT lpad(hex, 32, '0')::uuid AS uuid_val FROM ( VALUES ('ff'::text) , ('deadbeef') , ('ffffffffffffffff') , ('ffffffffffffffffffffffffffffffff') , ('ffffffffffffffffffffffffffffffff123') -- too long ) t(hex);
结果:
uuid_val -------------------------------------- 00000000-0000-0000-0000-0000000000ff 00000000-0000-0000-0000-0000deadbeef 00000000-0000-0000-ffff-ffffffffffff ffffffff-ffff-ffff-ffff-ffffffffffff ffffffff-ffff-ffff-ffff-ffffffffffff
正如你所看到的,标准输出是一个hex数字的string,典型的UUID分隔符。
MD5散列
这对于存储md5散列特别有用:
SELECT md5('Store hash for long string, maybe for index?')::uuid AS md5_hash
结果:
md5_hash -------------------------------------- 02e10e94-e895-616e-8e23-bb7f8025da42
如果其他人被困在PG8.2,这是另一种方式来做到这一点。
bigint版本:
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;
int版本:
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;