Bunm Jyo
Bunm Jyo's blog

Follow

Bunm Jyo's blog

Follow
postgresql 存储过程函数 随机字符指定规则生成

postgresql 存储过程函数 随机字符指定规则生成

Bunm Jyo's photo
Bunm Jyo
·Dec 1, 2022·

7 min read

内容比较多,可以自己逐一拨开,也可以一股脑的全部执行; 文件下载地址: FileDownload-ClickHere

使用方法后续给贴上

DO $$ begin raise notice '%',''||'========================== 华丽的标题分割线 ======base36_encode===================='; end; $$;
 -- 将int整型数值转为字符串型的36进制
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;

DO $$ begin raise notice '%',''||'========================== 华丽的分割线 =======base36_decode==================='; end; $$;


CREATE OR REPLACE FUNCTION base36_decode(IN base36 varchar)
   RETURNS bigint AS $$
DECLARE
   a char[];
   ret bigint;
   i int;
   val int;
   chars varchar;
BEGIN
   chars := '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

   FOR i IN REVERSE char_length(base36)..1 LOOP
         a := a || substring(upper(base36) FROM i FOR 1)::char;
      END LOOP;
   i := 0;
   ret := 0;
   WHILE i < (array_length(a,1)) LOOP
         val := position(a[i+1] IN chars)-1;
         ret := ret + (val * (36 ^ i));
         i := i + 1;
      END LOOP;

   RETURN ret;

END;
$$ LANGUAGE 'plpgsql' IMMUTABLE;

DO $$ begin raise notice '%',''||'========================== 华丽的分割线 =======random_string_32==================='; end; $$;
--select '华丽的分割线';
--  --

-- 获取总长度为re_length的字符串,其中除给定的prefix外,其他值均随机.
-- 随机范围 [0-9A-Z]
DROP FUNCTION IF EXISTS random_string_32( INTEGER, VARCHAR );
CREATE or replace FUNCTION random_string_32(re_length INTEGER, prefix CHARACTER VARYING)
   RETURNS TEXT

AS $$
DECLARE
   char_pool  TEXT [] := '{2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,J,K,L,M,N,P,Q,R,S,T,U,V,W,X,Y,Z}';
   result     TEXT := '';
   i          INTEGER := 0;
   prefix_len INTEGER :=0;
   random_len INTEGER :=0;
   idx        INTEGER :=0;
BEGIN

   IF prefix IS NOT NULL
   THEN
      prefix_len = char_length(prefix);
      result:=prefix;
   END IF;

   IF re_length < prefix_len
   THEN
      RAISE EXCEPTION 'The length of result cannot be less than the length of prefix';
   END IF;

   -- 随机字符串的长度 = 全长 - 前缀长度
   random_len := re_length - prefix_len;

   FOR i IN 1..random_len LOOP

         idx := floor(random() * 32) + 1;

         IF idx > 32
         THEN
            idx := 32;
         END IF;

         result := result || char_pool [idx];
      END LOOP;

   RETURN result;
END;
$$ LANGUAGE 'plpgsql' IMMUTABLE;

-- ========================== 华丽的分割线 ========================== --
DO $$ begin raise notice '%',''||'========================== 华丽的分割线 =======random_string_36==================='; end; $$;

-- 获取总长度为re_length的字符串,其中除给定的prefix外,其他值均随机.
-- 随机范围 [0-9A-Z]
-- DROP FUNCTION IF EXISTS random_string_36( INTEGER, VARCHAR );
CREATE OR REPLACE FUNCTION random_string_36(re_length INTEGER, prefix CHARACTER VARYING)
   RETURNS TEXT
AS $$
DECLARE
   char_pool  TEXT [] := '{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}';
   result     TEXT := '';
   i          INTEGER := 0;
   prefix_len INTEGER :=0;
   random_len INTEGER :=0;
   idx        INTEGER :=0;
BEGIN

   IF prefix IS NOT NULL
   THEN
      prefix_len = char_length(prefix);
      result:=prefix;
   END IF;

   IF re_length < prefix_len
   THEN
      RAISE EXCEPTION 'The length of result cannot be less than the length of prefix';
   END IF;

   -- 随机字符串的长度 = 全长 - 前缀长度
   random_len := re_length - prefix_len;

   FOR i IN 1..random_len LOOP

         idx := floor(random() * 36) + 1;

         IF idx = 37
         THEN
            idx := 36;
         END IF;

         result := result || char_pool [idx];
      END LOOP;

   RETURN result;
END;
$$ LANGUAGE 'plpgsql' IMMUTABLE;
-- // First migration.
-- Migration SQL that makes the change goes here.
-- ========================== 华丽的分割线 ========================== --

DO $$ begin raise notice '%',''||'========================== 华丽的分割线 =======fn_gen_glbsn==================='; end; $$;

-- // test : SELECT fn_gen_glbsn('RS',4,'seq_rs1_nfsn'); 即以RS为开头中间顺序递增字符长度4个length(且或者3),并指定seq的名称 其中nfsn意思是info seq 为 46,655 其中 dtsn 意思是 detail seq max为1679615
-- // 转化为字符串36 进制分别为 ZZZ和 ZZZZ 字符串
-- nfsn 信息订单 非每日增量-商品信息 员工信息 配置信息 单位信息
-- dtsn 详情序号 有每日增量-订单编号 订单详情
-- lgsn 日志序号

-- drop FUNCTION if exists fn_gen_glbsn(varchar(10),text);
-- drop FUNCTION if exists fn_gen_glbsn(varchar(10),text,integer);

CREATE OR REPLACE FUNCTION fn_gen_glbsn(
   serial_type VARCHAR(10) default 'UR',
   seq_name text default 'seq_user_dtsn', ----must be with nfsn-low dtsn-mid lgsn-high
   subf_length integer default 6, -- 后面的序号长度
   seq_number integer default 0 -- 序号的数值,默认不填,若填写,则使用手动指定序号值,否则由系统生成: 高并发场景下则由外部生成seq_number传入
)
   RETURNS VARCHAR(55) AS
$$
DECLARE
   var_countseq integer :=0;
   result         VARCHAR(55);
   milli_secs     INTEGER:=0;
   yearnumber     VARCHAR(10) := '18';
   weeknumber     VARCHAR(10) := '01';
   begenNfSeq     boolean:=false; -- info seq 信息序号 不会每天产生记录的序号
   begenDtSeq     boolean:=false; -- detail 详情序号/记录序号 即每天发生业务就会产生的
   begenLgSeq     boolean:=false; -- log seq 用于日志表的序号
   randomr_length integer     := 3;
   pre_lenth      integer     := 2;
   seq_length     integer:=3;
   -- // base36 60466175 = ZZZZZ
   -- // base36 1679615 = ZZZZ 4位 一个星期16.7w
   -- // base36 46655 = ZZZ 一个星期 nf
   maxvalseq      integer:=46655;
   --  select right(('00'||DATE_PART('doy',(current_date))),3) as doy;

   --  SELECT right(to_char(now()::date, 'YYYY'), 3) ;
BEGIN
   SELECT COUNT(1) into var_countseq FROM pg_class c WHERE c.relkind = 'S' and  relname = seq_name;
   --  ZZZ     nfsn 46655    4.67w / 每周 信息设置
   --  ZZZZ    dtsn 1679615  16.8w / 每周 订单记录,操作记录,订单详情
   --  ZZZZZ   lgsn 60466175 6000w / 每周 日志记录
   select (seq_name ~ 'nfsn') into begenNfSeq;

   select (seq_name ~ 'dtsn') into begenDtSeq;
   --       lgsn 60466175
   select (seq_name ~ 'lgsn') into begenLgSeq;

   if begenNfSeq then
      seq_length = 3;
   end if;

   if begenDtSeq then
      seq_length = 4;
   end if;

   if begenLgSeq then
      seq_length = 5;
   end if;

   if var_countseq < 1 then
      --  ZZZ     nfsn 46655    4.67w / 每周 信息设置
      --  ZZZZ    dtsn 1679615  16.8w / 每周 订单记录,操作记录,订单详情
      --  ZZZZZ   lgsn 60466175 6000w / 每周 日志记录

      -- info 46655
      if begenNfSeq then
         EXECUTE ('CREATE SEQUENCE IF NOT EXISTS '|| seq_name || ' START WITH 1 MINVALUE 1 INCREMENT 1 MAXVALUE 46655 CYCLE;');
      end if;

      -- detail
      if begenDtSeq then

         --          CREATE SEQUENCE IF NOT EXISTS seq_name START WITH 1 MINVALUE 1 INCREMENT 1 MAXVALUE 1679615 CYCLE;
         -- max value ZZZZ
         EXECUTE ('CREATE SEQUENCE IF NOT EXISTS '|| seq_name || ' START WITH 1 MINVALUE 1 INCREMENT 1 MAXVALUE 1679615 CYCLE;');

      end if;

      -- log
      if begenLgSeq then
         --          CREATE SEQUENCE IF NOT EXISTS seq_name START WITH 1 MINVALUE 1 INCREMENT 1 MAXVALUE 60466175 CYCLE;
         EXECUTE ('CREATE SEQUENCE IF NOT EXISTS '|| seq_name || ' START WITH 1 MINVALUE 1 INCREMENT 1 MAXVALUE 60466175 CYCLE;');

      end if;



      if (begenNfSeq::int + begenDtSeq::int + begenLgSeq::int ) = 0 then

         raise exception 'SEQ_NAME_IS_INVALID_MUST_BE_CONTAIN_NFSN_DTSN_LGSN';

      end if;

      --
   end if;

   SELECT right(to_char(now()::date, 'YYYY'), 2) into yearnumber;

   SELECT right('0' || ((SELECT EXTRACT(WEEK FROM now()))), 2) into weeknumber;

   -- 这里的seq_length 只能是3 or 4,3位seq长度即ZZZ=46655,4位seq长度即最大ZZZZ =1679615
   randomr_length = (subf_length - seq_length) + (pre_lenth - length(serial_type));
   -- 随机字符串长度为 给定值 减去序列长度 + 前缀长度2-前缀字符数

   -- 外部调用输入的seq_number 则不走内部自增序列直接使用外部序列
   if seq_number > 0 then
      SELECT INTO result serial_type || yearnumber || weeknumber || base36_encode( (seq_number + 0), seq_length) ||
                         random_string_32(randomr_length, '');

   else
      -- 前缀字符UR2+年份2+第几周2+序号36进制4+随机字符3
      SELECT INTO result serial_type || yearnumber || weeknumber || base36_encode((nextval(seq_name) + 0), seq_length) ||
                         random_string_32(randomr_length, '');

   end if;
   RETURN result;

END;
$$
   LANGUAGE 'plpgsql' VOLATILE;

--COMMENT ON FUNCTION fn_gen_glbsn(VARCHAR, text,integer) is '全局序列号';
--COMMENT ON FUNCTION fn_gen_glbsn(VARCHAR, text,integer,integer) is '全局序列号';

-- ========================== 华丽的分割线 ========================== --
-- 你可以执行一条测试 select fn_gen_glbsn('UR','seq_usr_nfsn');


-- 获取总长度为re_length的字符串,其中除给定的prefix外,其他值均随机.
-- 随机范围 [0-9A-Z]
DO $$ begin raise notice '%',''||'========================== 华丽的分割线 =======random_string_custype--be-create==================='; end; $$;

CREATE or replace FUNCTION random_string_custype (pooltype integer DEFAULT 0, re_length integer DEFAULT 6, prefix VARCHAR(10) DEFAULT 'A' ) RETURNS text
   LANGUAGE plpgsql
AS $$
DECLARE
   char_pool  TEXT []:='{}';
   result     TEXT := '';
   i          INTEGER := 0;
   prefix_len INTEGER :=0;
   random_len INTEGER :=0;
   idx        INTEGER :=0;
BEGIN

   CASE
      -- 0-9+大写A到Z
      WHEN pooltype = 36 THEN
         char_pool='{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}'; -- 36
   -- 0-9 数字
      WHEN pooltype = 10 THEN
         char_pool='{0,1,2,3,4,5,6,7,8,9}'; --10
   -- 数字+字母去掉0,1,I,O防止歧义
      WHEN pooltype = 32 THEN
         char_pool='{2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,J,K,L,M,N,P,Q,R,S,T,U,V,W,X,Y,Z}'; -- 32
      when pooltype = 33 then
         char_pool='{2,3,4,5,6,7,8,9,1,a,b,c,d,e,f,g,h,j,k,l,m,n,p,q,r,s,t,u,v,x,y,z}'; -- 32
      when pooltype = 46 then
         char_pool = '{あ,い,う,え,お,か,き,く,け,こ,さ,し,す,せ,そ,た,ち,つ,て,と,な,に,ぬ,ね,の,は,ひ,ふ,へ,ほ,ま,み,む,め,も,や,ゆ,よ,ら,り,る,れ,ろ,わ,を,ん}';
      when pooltype = 47 then -- 多了一个 ヱ
      char_pool = '{ア,イ,ウ,エ,オ,カ,キ,ク,ケ,コ,サ,シ,ス,セ,ソ,タ,チ,ツ,テ,ト,ナ,ニ,ヌ,ネ,ノ,ハ,ヒ,フ,ヘ,ホ,マ,ミ,ム,メ,モ,ヤ,ユ,ヨ,ラ,リ,ル,レ,ロ,ワ,ヲ,ヱ,ン}';
      WHEN pooltype = 55 THEN
         char_pool='{2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,J,K,L,M,N,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,j,k,m,n,p,q,r,s,t,u,v,w,x,y,z}';
      WHEN pooltype = 62 THEN
         char_pool='{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,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}';
      ELSE
         -- default is 32
         char_pool = '{2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,J,K,L,M,N,P,Q,R,S,T,U,V,W,X,Y,Z}';
      --  do nothing
      END CASE;
   if re_length < 8 then
      re_length = 8;
   end if;

   IF prefix IS NOT NULL
   THEN
      prefix_len = char_length(prefix);
      result:=prefix;
   END IF;

   IF re_length < prefix_len
   THEN
      RAISE EXCEPTION 'The length of result cannot be less than the length of prefix';
   END IF;

   -- 随机字符串的长度 = 全长 - 前缀长度
   random_len := re_length - prefix_len;

   FOR i IN 1..random_len LOOP

         idx := floor(random() * pooltype) + 1;

         IF idx = pooltype
         THEN
            idx := pooltype-1;
         END IF;

         result := result || char_pool [idx];
      END LOOP;

   RETURN result;
END;

$$;
--     LANGUAGE 'plpgsql' VOLATILE;

--     LANGUAGE 'plpgsql' VOLATILE;

DO $$ begin raise notice '%',''||'========================== 华丽的分割线 =======random_string_custype--created==================='; end; $$;

CREATE or replace FUNCTION random_string_presn (prefix varchar(55) default 'A', re_length integer DEFAULT 8,pooltype integer DEFAULT 32 ) RETURNS text
   LANGUAGE plpgsql
AS $$
DECLARE
   char_pool  TEXT []:='{}';
   result     TEXT := '';
   i          INTEGER := 0;
   prefix_len INTEGER :=0;
   random_len INTEGER :=0;
   idx        INTEGER :=0;
BEGIN

   select random_string_custype(pooltype,re_length,prefix||right(to_char(current_date, 'YYYY'), 3)||right(('00'||DATE_PART('doy',(current_date))),3)) into result;

   RETURN result;
END;

$$
--     LANGUAGE 'plpgsql' VOLATILE;
 
Share this