将文本表示形式中的十六进制转换为十进制数

8yparm6h  于 2022-09-21  发布在  PostgreSQL
关注(0)|答案(1)|浏览(258)

我正在尝试使用PostgreSQL 9.1将十六进制转换为十进制

使用此查询:

SELECT to_number('DEADBEEF', 'FMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');

我得到以下错误:

ERROR:  invalid input syntax for type numeric: " "

我做错了什么?

n6lpvg4x

n6lpvg4x1#

没有动态SQL的方式

text表示中的十六进制数字不会强制转换为数字类型,但我们可以使用bit(n)作为路点。存在从位串(bit(n))到整数类型(int2int4int8)的未记录转换--内部表示是二进制兼容的。Quoting Tom Lane:
这依赖于位类型输入转换器的一些未记录的行为,但我认为没有理由期望它会被打破。一个可能更大的问题是,它需要PG>=8.3,因为在此之前没有文本到位强制转换。

最大为integer。8位十六进制数字

最多可将8位十六进制数字转换为bit(32),然后强制转换为integer(标准4字节整数):

SELECT ('x' || lpad(hex, 8, '0'))::bit(32)::int 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

Postgres使用带符号整数类型,因此'7fffffff'以上的十六进制数会溢出为负整数数。这仍然是一种有效的、唯一的表示,但其含义有所不同。如果这很重要,请切换到bigint;见下文。

对于超过8位的十六进制数字,最低有效字符(超出右侧)将被截断

位串中的4位编码1个十六进制数字。已知长度的十六进制数可以直接转换为相应的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位十六进制数字

最多16位十六进制数字可以转换为bit(64),然后强制转换为bigint(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);
int8_val
---------------------
                 255
          2147483647
          2147483648
          3735928559
 9223372036854775807
-9223372036854775808
                  -1
                  -1

最大为uuid。32位十六进制数字

Postgres uuid数据类型不是数字类型。但它是标准Postgres中存储高达32位十六进制数字的最高效类型,仅占用16字节的存储空间。textuuid直接转换(不需要bit(n)作为路点),但需要正好32个十六进制数字。

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

如您所见,标准输出是一个带有UUID典型分隔符的十六进制数字字符串。

MD5哈希

这对于存储MD5哈希特别有用:

SELECT md5('Store hash for long string, maybe for index?')::uuid AS md5_hash;
md5_hash
--------------------------------------
 02e10e94-e895-616e-8e23-bb7f8025da42

请参见:

相关问题