【问题标题】:Hex string to integer conversion in Amazon RedshiftAmazon Redshift 中的十六进制字符串到整数的转换
【发布时间】:2014-01-20 05:37:12
【问题描述】:

Amazon Redshift 基于基于 Postgres 的 ParAccel。根据我的研究,在 Postgres 中执行十六进制字符串到整数转换的首选方法似乎是通过位字段,如answer 中所述。

对于 bigint,这将是:

select ('x'||lpad('123456789abcdef',16,'0'))::bit(64)::bigint

不幸的是,这在 Redshift 上失败了:

ERROR: cannot cast type text to bit [SQL State=42846] 

在 Postgres 8.1ish 中还有哪些其他方法可以执行此转换(接近 Redshift 的兼容性级别)? Redshift 不支持 UDF,数组、正则表达式函数或集合生成函数也不支持...

【问题讨论】:

  • 感谢您清楚地说明您正在使用 Redshift 并为我们保存“您真正使用的是什么”随机播放。现在,如果亚马逊愿意向sqlfiddle.com 捐赠一个 Redshift 实例,那么我们实际上可以在上面测试东西。我怀疑这个问题的最佳答案,就像 Redshift 上的大多数数据争论一样,将是“在客户端执行”。
  • @CraigRinger 我会通知亚马逊向 SQLFiddle 捐赠一个实例。
  • 很高兴看到,让手指和脚趾交叉。

标签: sql postgresql amazon-web-services hex amazon-redshift


【解决方案1】:

假设您想要一个简单的逐位序数位置转换(即您不担心二进制的恭维否定等)我认为这应该适用于 8.1 等效的数据库:

CREATE OR REPLACE FUNCTION hex2dec(text) RETURNS bigint AS $$
SELECT sum(CASE WHEN v >= ascii('a') THEN v - ascii('a') + 10 ELSE v - ascii('0') END * 16^ordpos)::bigint
FROM (
    SELECT n-1, ascii(substring(reverse($1), n, 1))
    FROM generate_series(1, length($1)) n
) AS x(ordpos, v);
$$ LANGUAGE sql IMMUTABLE;

函数形式是可选的,它只是更容易避免多次重复参数。无论如何它应该被内联。效率可能会很糟糕,但大多数可用于更智能地执行此操作的工具似乎不适用于旧版本,这至少有效:

regress=> CREATE TABLE t AS VALUES ('c13b'), ('a'), ('f');
regress=> SELECT hex2dec(column1) FROM t;
 hex2dec 
---------
   49467
      10
      15
(3 rows)

如果您可以使用regexp_split_to_arraygenerate_subscripts,它可能会更快。或者更慢。我没试过。另一个可能的技巧是使用数字映射数组而不是CASE,例如:

'[48:102]={0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,11,12,13,14,15}'::integer[]

您可以使用:

CREATE OR REPLACE FUNCTION hex2dec(text) RETURNS bigint AS $$
SELECT sum(
  ('[48:102]={0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,11,12,13,14,15}'::integer[])[ v ]
  * 16^ordpos
)::bigint
FROM (
    SELECT n-1, ascii(substring(reverse($1), n, 1))
    FROM generate_series(1, length($1)) n
) AS x(ordpos, v);
$$ LANGUAGE sql IMMUTABLE;

就我个人而言,我宁愿在客户端做,而不是争论旧 PostgreSQL 分支的有限功能,尤其是你不能加载自己的明智的用户定义的 C 函数或使用 PL/Perl,等等


在真正的 PostgreSQL 中,我只使用这个:

hex2dec.c

#include "postgres.h"
#include "fmgr.h"
#include "utils/builtins.h"
#include "errno.h"
#include "limits.h"
#include <stdlib.h>

PG_MODULE_MAGIC;

Datum from_hex(PG_FUNCTION_ARGS);

PG_FUNCTION_INFO_V1(hex2dec);

Datum
hex2dec(PG_FUNCTION_ARGS)
{
    char *endpos;
    const char *hexstr = text_to_cstring(PG_GETARG_TEXT_PP(0));
    long decval = strtol(hexstr, &endpos, 16);
    if (endpos[0] != '\0')
    {
        ereport(ERROR, (ERRCODE_INVALID_PARAMETER_VALUE, errmsg("Could not decode input string %s as hex", hexstr)));
    }
    if (decval == LONG_MAX && errno == ERANGE)
    {
        ereport(ERROR, (ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE, errmsg("Input hex string %s overflows int64", hexstr)));
    }
    PG_RETURN_INT64(decval);
}

生成文件

MODULES = hex2dec
DATA = hex2dec--1.0.sql
EXTENSION = hex2dec

PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

hex2dec.control

comment = 'Utility function to convert hex strings to decimal'
default_version = '1.0'
module_pathname = '$libdir/hex2dec'
relocatable = true

hex2dec--1.0.sql

CREATE OR REPLACE FUNCTION hex2dec(hexstr text) RETURNS bigint
        AS 'hex2dec','hex2dec'
        LANGUAGE c IMMUTABLE STRICT;

COMMENT ON FUNCTION hex2dec(hexstr text)
IS 'Decode the hex string passed, which may optionally have a leading 0x, as a bigint. Does not attempt to consider negative hex values.';

用法:

CREATE EXTENSION hex2dec;

postgres=# SELECT hex2dec('7fffffffffffffff');
       hex2dec       
---------------------
 9223372036854775807
(1 row)

postgres=# SELECT hex2dec('deadbeef');
  hex2dec   
------------
 3735928559
(1 row)

postgres=# SELECT hex2dec('12345');
 hex2dec 
---------
   74565
(1 row)

postgres=# select hex2dec(to_hex(-1));
  hex2dec   
------------
 4294967295
(1 row)

postgres=# SELECT hex2dec('8fffffffffffffff');
ERROR:  Input hex string 8fffffffffffffff overflows int64

postgres=# SELECT hex2dec('0x7abcz123');
ERROR:  Could not decode input string 0x7abcz123 as hex

性能差异......值得注意。给定样本数据:

CREATE TABLE randhex AS 
SELECT '0x'||to_hex( abs(random() * (10^((random()-.5)*10)) * 10000000)::bigint) AS h
FROM generate_series(1,1000000);

从使用 C 扩展名的暖缓存中从十六进制转换为十进制大约需要 1.3,这对于一百万行来说并不是很好。无需任何转换即可读取它们需要 0.95 秒。基于 SQL 的 hex2dec 方法处理相同的行需要 36 秒。坦率地说,SQL 方法的速度如此之快让我印象深刻,而 C ext 的速度如此之慢让我感到惊讶。

【讨论】:

  • Redshift 不支持 UDF,数组、正则表达式函数或集合生成函数也不支持...还有其他想法吗?
  • @Sim 使用客户端,如果 Redshift 不能做任何有用的事情,这听起来是唯一的选择。除非您可以提供对 Redshift 帐户的访问权限以进行持续测试,否则不能再摆弄它了,因为它显然除了基于古老的 PostgreSQL 之外还有各种限制。
【解决方案2】:

一个可能的解释是从textbit(n) 的转换依赖于未记录的行为,我重复quote from Tom Lane

这依赖于位类型输入的一些未记录的行为 转换器,但我认为没有理由期望它会中断。一个可能 更大的问题是它需要 PG >= 8.3,因为没有文本 在此之前进行位转换。

Amazon 衍生产品显然不允许这种未记录的功能。这并不奇怪,因为它基于 Postgres 8.1,根本没有演员表。

以前在这个密切相关的答案中引用:
Convert hex in text representation to decimal number

【讨论】:

  • 感谢您澄清该行为的原因。您对如何解决这个问题有什么想法吗?
【解决方案3】:

看起来他们在某个时候为此添加了一个函数:STRTOL

语法

STRTOL(num_string, base)

返回类型

大整数。如果 num_string 为 null,则返回 NULL。

例如

SELECT strtol('deadbeef', 16);

返回:3735928559

【讨论】:

  • 很高兴看到他们快速前进。
猜你喜欢
  • 2017-08-12
  • 1970-01-01
  • 1970-01-01
  • 2015-11-29
  • 2013-04-28
  • 2015-04-30
  • 2012-01-24
  • 2019-07-30
相关资源
最近更新 更多