Is there a function in PostgreSQL that can convert a base 10 number like 30
into a base 36 representation like u
?
Asked
Active
Viewed 9,589 times
14

Milen A. Radev
- 60,241
- 22
- 105
- 110

dan
- 43,914
- 47
- 153
- 254
-
ints are actually binary, base 10 is only used for communication with humans.. – Jasen May 23 '18 at 03:06
3 Answers
20
There are base-64 functions (such as encode
) but nothing for base-36. But you could write one of your own or 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;
I think you should ask yourself if the database is the right place for dealing with this sort of data formatting though, presentational issues like this might be better handled closer to final viewing level of your stack.

mu is too short
- 426,620
- 70
- 833
- 800
-
Unfortunately, the linked blog post is dead. You don't happen to have such an algorithm ready for an `IN digits bytea` representation for a longer number? :-) – Lukas Eder Oct 02 '13 at 15:09
-
@LukasEder: I'm not sure what you're asking. You have a `bytea` column that you want to encode as base-36 inside the database? – mu is too short Oct 02 '13 at 16:55
-
if you have a bytea you want something that encodes binary data with a sensible block size like `base32` instead. base36 gets just over 31 bits into 6 characters, great for signed `int32_t`, not so useful for binary data. a block size of 31 bytes=48 characters is kind of unweildy. – Jasen May 23 '18 at 03:30
-
you are correct, while I appreciate the solution, this sorta stuff should not be coded as functions into the db – PirateApp Mar 01 '19 at 07:35
-
-
0
here's a version that can take numbers of any size, it uses the data type "numeric" which is the postgresql implementation of bignum.
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;

Jasen
- 11,837
- 2
- 30
- 48
0
Modified Implementation
Modified from other implementation to increase the readability. Any kind of update or modification or suggestion appreciated to increase the readability.
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$;

Śhāhēēd
- 1,812
- 6
- 23
- 44