在不使用循环和游标的情况下计算自定义的ASCII值

后端开发 2026-07-08

我想把下面这个基于循环的查询(来自Oracle)转换为基于集合的查询(PostgreSQL),不使用循环/游标。

以下代码用于为给定字符串计算/生成数值。它先对字符串中的每个字母使用循环计算ASCII值,然后根据条件对其他值进行加减求和。

现有的基于循环的代码,我需要将其转换为基于集合的查询。

DECLARE
   str              VARCHAR2(500) := 'STACK101'; --INPUT STRING
   l                    PLS_INTEGER; 
   Cnt                PLS_INTEGER; 
   BALANCE_1   PLS_INTEGER := 1000001;
   BALANCE_2   PLS_INTEGER := 2000002;
   BALANCE_4   PLS_INTEGER := 4000004;   
   VAL_1       PLS_INTEGER := 0;
   VAL_2       PLS_INTEGER := 0;
   VAL_3       PLS_INTEGER := 0;
   R_1         PLS_INTEGER;
   R_2         PLS_INTEGER;
   BASE        PLS_INTEGER;
   OUT         NUMBER;
BEGIN

    Cnt := LENGTHC(str);

   FOR l IN 1 .. Cnt LOOP
      BASE := ASCII(SUBSTRC(str, l, 1));
      R_1  := VAL_1;
      R_2  := VAL_2;
      VAL_1 := 7 * VAL_3 + BASE;
      VAL_2 := 7 * R_1;
      VAL_3 := 7 * R_2;

      IF VAL_1 >= BALANCE_4 THEN
         VAL_1 := VAL_1 - BALANCE_4;
         VAL_2 := VAL_2 + 4;
      END IF;

      IF VAL_1 >= BALANCE_2 THEN
         VAL_1 := VAL_1 - BALANCE_2;
         VAL_2 := VAL_2 + 2;
      END IF;

      IF VAL_1 >= BALANCE_1 THEN
         VAL_1 := VAL_1 - BALANCE_1;
         VAL_2 := VAL_2 + 1;
      END IF;

      IF VAL_2 >= BALANCE_4 THEN
         VAL_2 := VAL_2 - BALANCE_4;
         VAL_3 := VAL_3 + 4;
      END IF;

      IF VAL_2 >= BALANCE_2 THEN
         VAL_2 := VAL_2 - BALANCE_2;
         VAL_3 := VAL_3 + 2;
      END IF;

      IF VAL_2 >= BALANCE_1 THEN
         VAL_2 := VAL_2 - BALANCE_1;
         VAL_3 := VAL_3 + 1;
      END IF;

      IF VAL_3 >= BALANCE_4 THEN
         VAL_3 := VAL_3 - BALANCE_4;
      END IF;

      IF VAL_3 >= BALANCE_2 THEN
         VAL_3 := VAL_3 - BALANCE_2;
      END IF;

      IF VAL_3 >= BALANCE_1 THEN
         VAL_3 := VAL_3 - BALANCE_1;
      END IF;
   END LOOP;

   OUT := VAL_3;
   OUT := BALANCE_1 * OUT + VAL_2;
   OUT := BALANCE_1 * OUT + VAL_1;
   DBMS_OUTPUT.PUT_LINE(OUT);
END;

期望输出:94874704955518361

解决方案

这里有一个Oracle的递归版本,你可以尝试把它转换为PostgreSQL:

with data(id, str) as (
    values
    ( 1, 'STACK101' ),
    ( 2, 'STACK102' )
),
rcte(id, idx, str, base, val_1, val_2, val_3, r_1, r_2, step) as (

    select id, 1 as idx, str, ASCII(SUBSTRC(str, 1, 1)) AS base,
        ASCII(SUBSTRC(str, 1, 1)) AS val_1, 0 AS val_2, 0 AS val_3,
        0 as r_1, 0 as r_2, 0 as step
    from data 

    union all

    select r.id, r.idx + decode(step, 10, 1, 0), r.str, 
        --base
        ASCII(SUBSTRC(r.str, r.idx+decode(step, 10, 1, 0), 1)), 
        -- val_1
        CASE WHEN r.step = 1 
            THEN 7 * r.val_3 + ASCII(SUBSTRC(r.str, r.idx+decode(step, 10, 1, 0), 1))
        WHEN r.step = 2 AND r.val_1 >= 4000004 
            THEN r.val_1 - 4000004
        WHEN r.step = 3 AND r.val_1 >= 2000002 
            THEN r.val_1 - 2000002
        WHEN r.step = 4 AND r.val_1 >= 1000001
            THEN r.val_1 - 1000001
        ELSE 
            r.val_1 
        END, 

        -- val_2
        CASE WHEN r.step = 1
            THEN 7 * r.r_1
        WHEN r.step = 2 AND r.val_1 >= 4000004 
            THEN r.val_2 + 4
        WHEN r.step = 3 AND r.val_1 >= 2000002 
            THEN r.val_2 + 2
        WHEN r.step = 4 AND r.val_1 >= 1000001
            THEN r.val_2 + 1
        WHEN r.step = 5 AND r.val_2 >= 4000004
            THEN r.val_2 - 4000004
        WHEN r.step = 6 AND r.val_2 >= 2000002
            THEN r.val_2 - 2000002
        WHEN r.step = 7 AND r.val_2 >= 1000001
            THEN r.val_2 - 1000001
        ELSE 
            r.val_2
        END, 

        -- val_3
        CASE 
        WHEN r.step = 1
            THEN 7 * r.r_2
        WHEN r.step = 5 AND r.val_2 >= 4000004
            THEN r.val_3 + 4
        WHEN r.step = 6 AND r.val_2 >= 2000002
            THEN r.val_3 + 2
        WHEN r.step = 7 AND r.val_2 >= 1000001
            THEN r.val_3 + 1
        WHEN r.step = 8 AND r.val_3 >= 4000004
            THEN r.val_3 - 4000004
        WHEN r.step = 9 AND r.val_3 >= 2000002
            THEN r.val_3 - 2000002
        WHEN r.step = 10 AND r.val_3 >= 1000001
            THEN r.val_3 - 1000001
        ELSE
            r.val_3
        END, 

        CASE 
        WHEN r.step = 10  
            THEN r.val_1
            ELSE r.r_1
        END, 
        CASE WHEN r.step = 10  
            THEN r.val_2
            ELSE r.r_2
        END,
        case when r.step = 10 then 0 else r.step+1 end
    from rcte r
    join data d on d.id = r.id and 
        case when r.step = 10 then r.idx < lengthc(r.str) else true end
)
select r.id, r.str,
    1000001 * (1000001 * (case when val_3 >= 1000001 then val_3 - 1000001 else val_3 end) + val_2) + val_1 
    as result
from rcte r
where r.idx = lengthc(r.str) and r.step = 10
order by id
;
1   STACK101    94874704955518361
2   STACK102    94874704955518362
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章