Oracle中金额数字转换为大写汉字

这个版本的函数,解决的问题

  1. 零出现重复或不出现的问题
  2. 当金额只有角或分的显示问题
  3. 金额为整数时,是否想要带 整 字
sql 复制代码
CREATE OR REPLACE FUNCTION NUM_TO_CAPITAL(MONEY IN NUMBER)
  RETURN VARCHAR2
IS
  TYPE STRING_ARRAY IS TABLE OF VARCHAR2(10);
  DIGIT_STR STRING_ARRAY := STRING_ARRAY('零','壹','贰','叁','肆','伍','陆','柒','捌','玖');
  
  V_NUM        VARCHAR2(100);
  V_INT        VARCHAR2(100);
  V_DEC        VARCHAR2(10);
  V_RESULT     VARCHAR2(300) := '';
  V_SECTION    VARCHAR2(4);
  V_SEC_RES    VARCHAR2(50) := '';
  V_I          NUMBER := 0;
  V_HAS_NUM    BOOLEAN;
  V_IS_OVER_WAN BOOLEAN := FALSE; -- 是否 >= 万元
BEGIN
  -- 1. 空值处理
  IF MONEY IS NULL THEN
    RETURN '';
  END IF;

  -- 2. 负数处理
  IF MONEY < 0 THEN
    RETURN '负' || NUM_TO_CAPITAL(ABS(MONEY));
  END IF;

  -- 3. 金额上限校验
  IF MONEY >= POWER(10,16) THEN
    RAISE_APPLICATION_ERROR(-20001, '金额超出转换范围(最大支持9999999999999999.99)');
  END IF;

  -- 4. 格式化拆分整数、小数
  V_NUM := TO_CHAR(ROUND(MONEY, 2), 'FM9999999999999990.00');
  V_INT := SUBSTR(V_NUM, 1, INSTR(V_NUM, '.') - 1);
  V_DEC := SUBSTR(V_NUM, INSTR(V_NUM, '.') + 1);

  -- 5. 整数部分为0时,不拼接"零元",直接处理小数
  IF V_INT != '0' THEN
    WHILE LENGTH(V_INT) > 0 LOOP
      IF LENGTH(V_INT) > 4 THEN
        V_SECTION := SUBSTR(V_INT, LENGTH(V_INT)-3, 4);
        V_INT     := SUBSTR(V_INT, 1, LENGTH(V_INT)-4);
      ELSE
        V_SECTION := LPAD(V_INT, 4, '0');
        V_INT     := '';
      END IF;

      V_SEC_RES := '';
      V_HAS_NUM := FALSE;

      -- 逐位转换:仟、佰、拾、个
      FOR J IN 1..4 LOOP
        DECLARE
          D  CHAR(1) := SUBSTR(V_SECTION, J, 1);
          U  VARCHAR2(2) := CASE J WHEN 1 THEN '仟' WHEN 2 THEN '佰' WHEN 3 THEN '拾' ELSE '' END;
        BEGIN
          IF D != '0' THEN
            IF V_SEC_RES IS NOT NULL AND SUBSTR(V_SECTION, J-1, 1) = '0' THEN
              V_SEC_RES := V_SEC_RES || '零';
            END IF;
            V_SEC_RES := V_SEC_RES || DIGIT_STR(TO_NUMBER(D)+1) || U;
            V_HAS_NUM := TRUE;
          END IF;
        END;
      END LOOP;

      -- 追加单位:万、亿、万亿
      IF V_HAS_NUM THEN
        CASE V_I
          WHEN 1 THEN 
            V_SEC_RES := V_SEC_RES || '万'; 
            V_IS_OVER_WAN := TRUE;
          WHEN 2 THEN 
            V_SEC_RES := V_SEC_RES || '亿'; 
            V_IS_OVER_WAN := TRUE;
          WHEN 3 THEN 
            V_SEC_RES := V_SEC_RES || '万亿'; 
            V_IS_OVER_WAN := TRUE;
          ELSE NULL;
        END CASE;
        V_RESULT := V_SEC_RES || V_RESULT;
      ELSIF V_I = 1 AND V_RESULT IS NOT NULL THEN
        IF SUBSTR(V_RESULT, 1, 1) != '零' THEN
          V_RESULT := '零' || V_RESULT;
        END IF;
      END IF;

      V_I := V_I + 1;
    END LOOP;

    -- 整数部分收尾
    IF V_RESULT IS NOT NULL THEN
      V_RESULT := V_RESULT || '元';
    END IF;
  END IF;

  -- 6. 小数部分处理
  IF V_DEC != '00' THEN
    -- 角
    IF SUBSTR(V_DEC,1,1) != '0' THEN
      V_RESULT := V_RESULT || DIGIT_STR(TO_NUMBER(SUBSTR(V_DEC,1,1))+1) || '角';
    ELSIF SUBSTR(V_DEC,2,1) != '0' AND V_RESULT IS NOT NULL THEN
      V_RESULT := V_RESULT || '零';
    END IF;
    -- 分
    IF SUBSTR(V_DEC,2,1) != '0' THEN
      V_RESULT := V_RESULT || DIGIT_STR(TO_NUMBER(SUBSTR(V_DEC,2,1))+1) || '分';
    END IF;
  ELSE
    -- 核心规则:只有 ≥ 万元 且无小数时才加"整"
    IF V_RESULT IS NOT NULL AND V_IS_OVER_WAN THEN
      V_RESULT := V_RESULT || '整';
    END IF;
  END IF;

  -- 7. 处理全零金额
  IF V_RESULT IS NULL OR V_RESULT = '' THEN
    RETURN '零元';
  END IF;

  RETURN V_RESULT;
END NUM_TO_CAPITAL;
相关推荐
bksczm3 分钟前
MySQL之读写分离与分库分表
数据库·mysql
姜鱼问生5 分钟前
报数据库连不上?Docker Compose 启动顺序假象:depends_on 不是万能的!
数据库·docker·容器
梅孔立13 分钟前
.mdb Access_MDB历史数据文件迁移MySQL实战教程-可直接投喂给ai参考这个帮你完成迁移
数据库·mysql·adb
时间售卖铺34 分钟前
MySQL 数据库账户及授权
运维·数据库·mysql·云原生
l1t35 分钟前
pgrust v0.4-beta 安装和查询测试
数据库·postgresql·rust·duckdb·pgrust
SelectDB技术团队35 分钟前
SelectDB 架构演进与 ClickHouse 迁移指南
大数据·数据库·clickhouse·云原生·架构·数据分析·apache doris
java_nnnn1 小时前
分布式系统进阶:数据库分库分表、微服务与架构演进总结
数据库·redis·微服务·架构
ZENERGY-众壹1 小时前
光伏监控平台实测,三款时序数据库在 50 万测点下的真实表现
数据库·架构·运维开发·时序数据库·光伏运维
catoop11 小时前
API Key 设计与安全生成方案:结构拆解、数据库设计与 Java 实现
java·数据库·安全