在oracle plsql中从ulid提取时间戳

问题描述

是否有一种方法可以从Oracle PL / SQL中的ULID数据中提取时间戳?最近,我们不得不与一家供应商合作,该供应商使用ULID为变更数据提供唯一的变更序列标识符,并且需要提取时间戳。如创建者的github所述,解决方案还可以使用其他语言,但是我们希望能够在Oracle中运行转换。

解决方法

这花费了太长时间才能实施,我想节省别人尝试自己做的时间

DECLARE
    p_Ulid VARCHAR2(200) := '01EG664DVCY5NTH7WFN57PA7TM'; --Example ULID
    FUNCTION Get_Ulid_Ts(p_In VARCHAR2) RETURN TIMESTAMP
        WITH TIME ZONE IS
        Dec_Value   NUMBER := 0;
        t_Time_Part VARCHAR2(10) := Substr(p_In,10); --First 10 characters are the timestamp
        Ret         TIMESTAMP WITH TIME ZONE := To_Timestamp_Tz('19700101 +00:00','yyyymmdd TZH:TZM'); --Unix timestamp sentinal
        c_Base      NUMBER := 32;
        c_Base32    VARCHAR2(32) := '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; --Crockford's base32
        TYPE B32_Map_Typ IS TABLE OF NUMBER INDEX BY VARCHAR2(1);
        B32map B32_Map_Typ;
    BEGIN
        --initialize base32 map
        FOR i IN 0 .. Length(c_Base32) - 1
        LOOP
            B32map(Substr(c_Base32,i + 1,1)) := i;
        END LOOP;
        --convert base 32 to base 10
        FOR i IN 1 .. Length(t_Time_Part)
        LOOP
            Dec_Value := Dec_Value +
                         Power(c_Base,i - 1) *
                         B32map(Substr(t_Time_Part,-i,1));
        END LOOP;
        --add to unix timestamp sentinal
        Ret := Ret +numtodsinterval(((Dec_Value/ 1000) ),'SECOND') ;
        RETURN Ret;
    END;
BEGIN
    --ISO8601 timestamp formats
    EXECUTE IMMEDIATE q'!alter session set nls_timestamp_format = 'YYYY-MM-DD"T"HH24:MI:SS.ff3"Z"' !';
    EXECUTE IMMEDIATE q'!alter session set nls_timestamp_tz_format = 'YYYY-MM-DD"T"HH24:MI:SS.ff3 TZR' !';
    --test function
    Dbms_Output.Put_Line(Get_Ulid_Ts(p_Ulid));
    Dbms_Output.Put_Line(Get_Ulid_Ts(p_Ulid) At TIME ZONE
                         'America/New_York');
END;
/

如果有人有更高效的服务,请发表评论。 (我也没有足够的声誉在StackOverflow中创建“ ulid”标签)

以下是一个包​​装变体,可以稍微改善性能:

CREATE OR REPLACE PACKAGE LUTOOLS.Ulid IS

    Nls_Timestamp_Format    VARCHAR2(64) := 'YYYY-MM-DD"T"HH24:MI:SS.ff3"Z"';
    Nls_Timestamp_Tz_Format VARCHAR2(64) := 'YYYY-MM-DD"T"HH24:MI:SS.ff3"Z"';

    TYPE B32_Map_Typ IS TABLE OF NUMBER INDEX BY VARCHAR2(1);
    B32map B32_Map_Typ;

    c_Base   NUMBER := 32;
    c_Base32 VARCHAR2(32) := '0123456789ABCDEFGHJKMNPQRSTVWXYZ'; --Crockford's base32

    FUNCTION Get_Ulid_Ts(p_In VARCHAR2) RETURN TIMESTAMP
        WITH TIME ZONE;

END Ulid;
/

CREATE OR REPLACE PACKAGE BODY LUTOOLS.Ulid IS

    FUNCTION Get_Ulid_Ts(p_In VARCHAR2) RETURN TIMESTAMP
        WITH TIME ZONE IS
        Dec_Value   NUMBER := 0;
        t_Time_Part VARCHAR2(10) := Substr(p_In,'yyyymmdd TZH:TZM');
    
    BEGIN
        --convert base 32 to base 10
        FOR i IN 1 .. Length(t_Time_Part)
        LOOP
            Dec_Value := Dec_Value +
                         Power(c_Base,1));
        END LOOP;
        --add to unix timestamp sentinal
        Ret := Ret + Numtodsinterval(((Dec_Value / 1000)),'SECOND');
        RETURN Ret;
    END;

BEGIN
    --initialize base32 map
    FOR i IN 0 .. Length(c_Base32) - 1
    LOOP
        B32map(Substr(c_Base32,1)) := i;
    END LOOP;

END Ulid;
/

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...