我正在尝试使用PostgreSQL 9.1将十六进制转换为十进制。
使用以下查询:
SELECT to_number('DEADBEEF', 'FMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
我得到了以下错误:
ERROR: invalid input syntax for type numeric: " "
我做错了什么?
我正在尝试使用PostgreSQL 9.1将十六进制转换为十进制。
使用以下查询:
SELECT to_number('DEADBEEF', 'FMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
我得到了以下错误:
ERROR: invalid input syntax for type numeric: " "
我做错了什么?
在text
表示中,没有从十六进制数转换为数字类型的转换,但我们可以使用bit(n)
作为中间步骤。有一些未记录的从位字符串(bit(n)
)到整数类型(int2
, int4
, int8
)的转换 - 内部表示是二进制兼容的。 引用Tom Lane:
这依赖于位类型输入转换器的某些未记录行为,但我认为没有理由指望它会出错。可能更大的问题是它需要PG >= 8.3,因为在此之前没有文本到位的转换。
integer
适用于最多8个十六进制数字最多可以将8个十六进制数字转换为bit(32)
,然后强制转换为integer
(标准4字节整数):
SELECT <b>('x' || lpad(hex, 8, '0'))::bit(32)::int</b> AS int_val
FROM (
VALUES
('1'::text)
, ('f')
, ('100')
, ('7fffffff')
, ('80000000') -- overflow into negative number
, ('deadbeef')
, ('ffffffff')
, ('ffffffff123') -- too long
) AS t(hex);
int_val
------------
1
15
256
2147483647
-2147483648
-559038737
-1
'7fffffff'
会溢出成负整数。这仍然是一个有效的、独特的表示,但是含义不同。如果这很重要,请切换到bigint
;请参见下文。bit(n)
。或者,将长度未知的十六进制数字用前导零(0
)填充,如演示所示,并转换为bit(32)
。例如,使用7个十六进制数字和int
或8个数字和bigint
的示例:SELECT ('x'|| 'deafbee')::bit(28)::int
, ('x'|| 'deadbeef')::bit(32)::bigint;
int4 | int8
-----------+------------
233503726 | 3735928559
bigint
最多可以将16位十六进制数字转换为bit(64)
,然后强制转换为bigint
(int8
,即8字节整数) - 再次溢出为负数:
SELECT <b>('x' || lpad(hex, 16, '0'))::bit(64)::bigint</b> AS int8_val
FROM (
VALUES
('ff'::text)
, ('7fffffff')
, ('80000000')
, ('deadbeef')
, ('7fffffffffffffff')
, ('8000000000000000') -- overflow into negative number
, ('ffffffffffffffff')
, ('ffffffffffffffff123') -- too long
) t(hex);
int8_val
---------------------
255
2147483647
2147483648
3735928559
9223372036854775807
-9223372036854775808
-1
-1
uuid
用于最多32个十六进制数字Postgres uuid
数据类型不是数值类型。但它是标准Postgres中存储最多32个十六进制数字最有效的类型,仅占用16字节的存储空间。可以从text
直接转换为uuid
(无需使用bit(n)
作为航路点),但需要确切地32个十六进制数字。
SELECT <b>lpad(hex, 32, '0')::uuid</b> 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
如您所见,标准输出是一个带有UUID典型分隔符的十六进制数字字符串。
这对于存储md5哈希值特别有用:
SELECT md5('Store hash for long string, maybe for index?')::uuid AS md5_hash;
md5_hash
--------------------------------------
02e10e94-e895-616e-8e23-bb7f8025da42
请参见:
pg-bignum
的另一种方法 https://dev59.com/cmsy5IYBdhLWcg3wyxCi#47724472 - Evan Carroll你有两个紧急问题:
to_number
不理解十六进制。X
在 to_number
格式字符串中没有任何意义,而且没有意义的字符显然意味着“跳过一个字符”。对于问题(2),我没有权威的理由,只有经验证据:
=> SELECT to_number('123', 'X999');
to_number
-----------
23
(1 row)
=> SELECT to_number('123', 'XX999');
to_number
-----------
3
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;
接下来:
=> select hex_to_int('DEADBEEF');
hex_to_int
------------
-559038737 **
(1 row)
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
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_in_hex('deadbeef')::text::numeric
将类型转换为数字。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;
这里是另一种实现:
CREATE OR REPLACE FUNCTION hex_to_decimal3(hex_string text)
RETURNS numeric
LANGUAGE plpgsql
IMMUTABLE
AS $function$
declare
hex_string_lower text := lower(hex_string);
i int;
digit int;
s numeric := 0;
begin
for i in 1 .. length(hex_string) loop
digit := position(substr(hex_string_lower, i, 1) in '0123456789abcdef') - 1;
if digit < 0 then
raise '"%" is not a valid hexadecimal digit', substr(hex_string_lower, i, 1) using errcode = '22P02';
end if;
s := s * 16 + digit;
end loop;
return s;
end
$function$;
这是一个简单的函数,逐位处理数字,在使用position()
函数计算输入字符串中每个字符的数值。相对于hex_to_decimal2()
的优点在于它似乎更快(对于md5()
生成的十六进制字符串快了4倍左右)。
CREATE OR REPLACE FUNCTION numeric_from_bytes(bytea)
RETURNS numeric
LANGUAGE plpgsql
AS $$
declare
bits bit varying;
result numeric := 0;
exponent numeric := 0;
bit_pos integer;
begin
execute 'SELECT x' || quote_literal(substr($1::text,3)) into bits;
bit_pos := length(bits) + 1;
exponent := 0;
while bit_pos >= 56 loop
bit_pos := bit_pos - 56;
result := result + substring(bits from bit_pos for 56)::bigint::numeric * pow(2::numeric, exponent);
exponent := exponent + 56;
end loop;
while bit_pos >= 8 loop
bit_pos := bit_pos - 8;
result := result + substring(bits from bit_pos for 8)::bigint::numeric * pow(2::numeric, exponent);
exponent := exponent + 8;
end loop;
return trunc(result);
end;
$$;
CREATE OR REPLACE FUNCTION numeric_from_bytes(bytea)
RETURNS numeric
LANGUAGE sql
AS $$
SELECT ('0'||right($1::text,-1))::numeric
$$;
这里有另一个版本,它使用数字,因此可以处理任意大的十六进制字符串:
create OR REPLACE function hex_to_decimal2(hex_string text)
returns text
language plpgsql immutable as $pgsql$
declare
bits bit varying;
result numeric := 0;
begin
execute 'SELECT x' || quote_literal(hex_string) INTO bits;
while length(bits) > 0 loop
result := result + (substring(bits from 1 for 1)::bigint)::numeric * pow(2::numeric, length(bits) - 1);
bits := substring(bits from 2 for length(bits) - 1);
end loop;
return trunc(result, 0);
end
$pgsql$;
例如:
=# select hex_to_decimal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
32592575621351777380295131014550050576823494298654980010178247189670100796213387298934358015
例如:
=# select hex_to_decimal('5f68e8131ecf80000');
110000000000000000000
SELECT convert_from('\x7468697320697320612076657279206C6F6E672068657820737472696E67','utf8')
返回
this is a very long hex string
create or replace function ${migrSchemaName4G}.hex_to_decimal(hex_string text)
returns numeric
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(0, length(bits) - chunk_size) + 1;
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 result;
end
$pgsql$;
MOD(TO_NUMBER(standard_hash(ID, 'MD5'), 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'), ${processCount}) = ${processNumber}
对于Postgres - hex_to_Decimal(MD5(ID)) % ${Processcount} = ${Processnumber}