【发布时间】:2011-05-13 20:12:52
【问题描述】:
PostgreSQL 中是否有一个函数可以将基数为 10 的数字(如 30)转换为基数为 36 的表示(如 u)?
【问题讨论】:
-
ints 实际上是二进制的,base 10 仅用于与人类交流..
标签: postgresql
PostgreSQL 中是否有一个函数可以将基数为 10 的数字(如 30)转换为基数为 36 的表示(如 u)?
【问题讨论】:
标签: postgresql
有 base-64 函数(例如 encode),但 base-36 没有。但你可以自己写一个或use this one:
CREATE OR REPLACE FUNCTION base36_encode(IN digits bigint, IN min_width int = 0) RETURNS varchar AS $$
DECLARE
chars char[];
ret varchar;
val bigint;
BEGIN
chars := ARRAY['0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
val := digits;
ret := '';
IF val < 0 THEN
val := val * -1;
END IF;
WHILE val != 0 LOOP
ret := chars[(val % 36)+1] || ret;
val := val / 36;
END LOOP;
IF min_width > 0 AND char_length(ret) < min_width THEN
ret := lpad(ret, min_width, '0');
END IF;
RETURN ret;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
我认为您应该问问自己,数据库是否是处理此类数据格式的正确位置,但在接近堆栈的最终查看级别时,可能会更好地处理此类表示问题。
【讨论】:
IN digits bytea 表示更长的数字准备好这样的算法吗? :-)
bytea 列,你想在数据库中编码为base-36?
base32 这样的合理块大小来编码二进制数据的东西。 base36 将超过 31 位转换为 6 个字符,非常适合带符号的 int32_t,但对于二进制数据不太有用。 31 字节 = 48 个字符的块大小有点笨拙。
abs 函数,这样可以节省几行代码。
这是一个可以接受任意大小数字的版本,它使用数据类型“numeric”,它是 bignum 的 postgresql 实现。
CREATE OR REPLACE FUNCTION base36_encode(IN digits numeric, IN min_width int = 0) RETURNS text AS $$
DECLARE
chars char[] := ARRAY['0','1','2','3','4','5','6','7','8','9','A','B'
,'C','D','E','F','G','H','I','J','K','L','M','N'
,'O','P','Q','R','S','T','U','V','W','X','Y','Z' ] ;
ret text:='';
val numeric:= digits;
BEGIN
IF digits < 0 THEN
val := -val;
END IF;
WHILE val > 0 OR min_width > 0 LOOP
ret := chars[(mod(val,36))+1] || ret;
val := div(val,36);
min_width := min_width-1;
END LOOP;
IF digits < 0 THEN
ret := '-'||ret;
END IF;
RETURN ret;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
【讨论】:
从其他实现修改以增加可读性。任何类型的更新或修改或建议都可以增加可读性。
CREATE OR REPLACE FUNCTION fn_base36_encode(IN base10 bigint)
RETURNS varchar
LANGUAGE plpgsql
AS $BODY$
DECLARE
base36 varchar := '';
intval bigint := abs(base10);
char0z char[] := regexp_split_to_array('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ', '');
BEGIN
WHILE intval != 0 LOOP
base36 := char0z[(intval % 36)+1] || base36;
intval := intval / 36;
END LOOP;
IF base10 = 0 THEN base36 := '0'; END IF;
RETURN base36;
END;
$BODY$;
【讨论】: