Har*_*wig 33 postgresql hex types casting
我试图使用PostgreSQL 9.1将十六进制转换为十进制
使用此查询:
SELECT to_number('DEADBEEF', 'FMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
Run Code Online (Sandbox Code Playgroud)
我收到以下错误:
ERROR: invalid input syntax for type numeric: " "
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?
Erw*_*ter 68
有没有动态SQL的方法.
从text表示中的十六进制数字到数字类型没有强制转换,但我们可以bit(n)用作航点.位串中的4位编码1个十六进制数字.从位串到bit(32)最多(最多8个十六进制数字)到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);Run Code Online (Sandbox Code Playgroud)
结果:
int_val
------------
1
15
256
2147483647
-2147483648
-559038737
-1
Run Code Online (Sandbox Code Playgroud)
4个字节足以编码 最多8位数的所有十六进制数,但integer在Postgres中是有符号类型,因此上面的'7fffffff'十六进制数溢出为负数int.这仍然是一种独特的表现形式,但意义不同.如果重要的话bigint,请参见下文.
对于未知变化长度的十六进制数,我们需要填充前导零,0如演示所示bit(32).对于已知长度的数字,我们可以调整长度说明符.7个十六进制数字和/ int或8位数的示例bigint:
SELECT ('x'|| 'deafbee')::bit(28)::int
, ('x'|| 'deadbeef')::bit(32)::bigint;
int4 | int8
-----------+------------
233503726 | 3735928559
Run Code Online (Sandbox Code Playgroud)
使用bigint(int8,8字节整数)最多16个十六进制数字 - 溢出到上半部分的负数:
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);Run Code Online (Sandbox Code Playgroud)
结果:
int8_val
---------------------
255
2147483647
2147483648
3735928559
9223372036854775807
-9223372036854775808
-1
-1
Run Code Online (Sandbox Code Playgroud)
对于超过16个十六进制数字,最不重要的字符(超出右侧)会被截断.
这依赖于位型输入转换器的一些未记录的行为,但我认为没有理由期望会破坏.一个可能更大的问题是它需要PG> = 8.3,因为在此之前没有要进行比特转换的文本.
Postgres uuid数据类型不是数字类型,因此这与提出的问题不同.但它是标准Postgres中最有效的类型,可存储多达32个十六进制数字,仅占用16个字节的存储空间.有一个直接演员,但需要正好 32个十六进制数字.
SELECT lpad(hex, 32, '0')::uuid AS uuid_val
FROM (
VALUES ('ff'::text)
, ('deadbeef')
, ('ffffffffffffffff')
, ('ffffffffffffffffffffffffffffffff')
, ('ffffffffffffffffffffffffffffffff123') -- too long
) t(hex);Run Code Online (Sandbox Code Playgroud)
结果:
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
Run Code Online (Sandbox Code Playgroud)
如您所见,标准输出是一个十六进制数字字符串,其中包含UUID的典型分隔符.
这对于存储md5哈希特别有用:
SELECT md5('Store hash for long string, maybe for index?')::uuid AS md5_hash
Run Code Online (Sandbox Code Playgroud)
结果:
md5_hash
--------------------------------------
02e10e94-e895-616e-8e23-bb7f8025da42
Run Code Online (Sandbox Code Playgroud)
mu *_*ort 20
你有两个直接的问题:
to_number 不懂十六进制.X在to_number格式字符串中没有任何意义,任何没有意义的东西显然意味着"跳过一个字符".我没有(2)的权威理由,只是经验证据:
=> SELECT to_number('123', 'X999');
to_number
-----------
23
(1 row)
=> SELECT to_number('123', 'XX999');
to_number
-----------
3
Run Code Online (Sandbox Code Playgroud)
该文档提到了双引号模式应该如何表现:
In
to_date,to_number和to_timestamp,双引号字符串跳过字符串中包含的输入字符数,例如"XX"跳过两个输入字符.
但是,未指定格式化字符的非引用字符的行为似乎未指定.
在任何情况下,to_number不是将十六进制转换为数字的正确工具,你想说这样的事情:
select x'deadbeef'::int;
Run Code Online (Sandbox Code Playgroud)
所以也许这个功能对你来说会更好:
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;
Run Code Online (Sandbox Code Playgroud)
然后:
=> select hex_to_int('DEADBEEF');
hex_to_int
------------
-559038737 **
(1 row)
Run Code Online (Sandbox Code Playgroud)
**要避免整数溢出错误中的负数,请使用bigint而不是int来容纳更大的十六进制数(如IP地址).
这是一个使用 的版本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$;
Run Code Online (Sandbox Code Playgroud)
例如:
=# select hex_to_decimal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
32592575621351777380295131014550050576823494298654980010178247189670100796213387298934358015
Run Code Online (Sandbox Code Playgroud)
如果其他人被 PG8.2 困住了,这里有另一种方法。
大整数版本:
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;
Run Code Online (Sandbox Code Playgroud)
国际版本:
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;
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
您可以使用bn_in_hex('deadbeef')::text::numeric.
| 归档时间: |
|
| 查看次数: |
37582 次 |
| 最近记录: |