Oracle把阿拉伯数字转换为汉字

在 Oracle 中创建函数

sql 复制代码
CREATE OR REPLACE FUNCTION F_NUM_TO_CHN(p_num IN NUMBER) RETURN VARCHAR2 IS
  v_num       VARCHAR2(50);
  v_len       NUMBER;
  v_res       VARCHAR2(4000) := '';
  v_digit     NUMBER;
  v_char      VARCHAR2(4);
  v_unit      VARCHAR2(4);
  v_is_zero   BOOLEAN;
  
  -- 【核心修复】v_need_zero 标记位:记录上一位是否是零,且尚未输出"零"
  v_need_zero BOOLEAN := FALSE; 
  
  v_chars CONSTANT VARCHAR2(20) := '零一二三四五六七八九';
BEGIN
  IF p_num IS NULL THEN RETURN NULL; END IF;
  IF p_num = 0 THEN RETURN '零'; END IF;
  IF p_num < 0 THEN RETURN '负' || F_NUM_TO_CHN(ABS(p_num)); END IF;
  
  v_num := TO_CHAR(p_num);
  v_len := LENGTH(v_num);
  
  FOR i IN 1 .. v_len LOOP
    v_digit := TO_NUMBER(SUBSTR(v_num, i, 1));
    v_is_zero := (v_digit = 0);
    
    -- 1. 确定当前位的单位
    CASE (v_len - i)
      WHEN 0 THEN v_unit := '';
      WHEN 1 THEN v_unit := '十';
      WHEN 2 THEN v_unit := '百';
      WHEN 3 THEN v_unit := '千';
      WHEN 4 THEN v_unit := '万';
      WHEN 5 THEN v_unit := '十';
      WHEN 6 THEN v_unit := '百';
      WHEN 7 THEN v_unit := '千';
      WHEN 8 THEN v_unit := '亿';
      WHEN 9 THEN v_unit := '十';
      WHEN 10 THEN v_unit := '百';
      WHEN 11 THEN v_unit := '千';
      WHEN 12 THEN v_unit := '万亿';
      ELSE v_unit := ''; 
    END CASE;
    
    IF v_is_zero THEN
      -- 当前位是 0
      -- 标记需要补零(但先不补,等到遇到下一个非零数字再补)
      v_need_zero := TRUE;
    ELSE
      -- 当前位是非零数字
      
      -- 【核心逻辑】检查是否需要补"零"
      IF v_need_zero THEN
        -- 特殊情况处理:如果是"十"位,且前面全是0(即数字是 10, 100010 这种结构的十位)
        -- 规则:10->十 (不补零,也不读一); 101->一百零一 (补零)
        -- 判断条件:如果单位是"十",且这是该节的第一个非零数(即前一位是0,且再前一位也是0或不存在)
        -- 简单判断:如果结果是空,或者结果末尾是"万"/"亿",且单位是"十",且数字是1 -> 不补零,不读一
        
        v_char := SUBSTR(v_chars, v_digit + 1, 1);
        
        -- 特殊场景:10-19 的开头 (如 10, 11, 1010)
        -- 如果单位是"十",且数字是"一",且 (前面没有数字 或者 前面刚结束了"万/亿"节)
        -- 此时不需要补"零",也不需要读"一",直接读"十"
        IF v_unit = '十' AND v_digit = 1 THEN
           IF v_res = '' OR SUBSTR(v_res, -1) IN ('万', '亿', '零') THEN
              -- 情况 A: 开头是 1 (如 12 -> 十二) -> 不补零,不加"一",只加"十"
              -- 情况 B: 万/亿后是 1 (如 10010 -> 一万零十? 不对,是一万零一十? 中文习惯是一万零一十)
              -- 修正:10010 -> 一万零一十。只有当它是整个数的最高位时(10-19),才省略"一"。
              
              IF v_res = '' THEN
                 -- 整个数的最高位是十位且为1 (10-19) -> 省略"一",且不补零
                 v_res := v_res || v_unit;
                 v_need_zero := FALSE; -- 消耗掉零标记(其实前面也没零)
                 CONTINUE; -- 跳过后续添加逻辑
              ELSIF SUBSTR(v_res, -1) = '零' THEN
                 -- 前面已经补过零了 (如 101 -> 一百零...),这里直接加"十"
                 v_res := v_res || v_unit;
                 v_need_zero := FALSE;
                 CONTINUE;
              ELSIF SUBSTR(v_res, -1) IN ('万', '亿') THEN
                 -- 万/亿后面紧跟十位 (如 10010 -> 一万... 这里的逻辑稍微复杂)
                 -- 10010: 1(万) 0 0 1(十) 0 -> 一万零一十
                 -- 10000: 一万
                 -- 10012: 一万零一十二
                 -- 如果前面是万/亿,且中间有0 (v_need_zero为真),则必须先补零
                 v_res := v_res || '零' || v_char || v_unit;
                 v_need_zero := FALSE;
                 CONTINUE;
              END IF;
           END IF;
        END IF;

        -- 通用情况:补"零"
        v_res := v_res || '零';
        v_need_zero := FALSE; -- 标记已消费
      END IF;
      
      -- 添加当前数字和单位
      v_char := SUBSTR(v_chars, v_digit + 1, 1);
      
      -- 再次处理 10-19 省略"一"的情况 (针对非零补位后的情况)
      -- 如果单位是"十",数字是"一",且它是该节的最高位(即前面没有百/千,或者前面是万/亿且没补零?)
      -- 简化规则:如果单位是"十",数字是"一",且 (结果是空 或者 结果末尾是"零"/"万"/"亿" 且 刚刚没补零?)
      -- 最稳妥的省略规则:仅当"一十"出现在字符串的最开头时省略。其他情况(如一百一十、一万零一十)通常保留"一"或根据口语习惯。
      -- 标准普通话:110 -> 一百一十 (省略一); 1010 -> 一千零一十 (省略一? 不,是一千零一十).
      -- 实际上:11-19 在十位上通常省略"一"。
      
      IF v_unit = '十' AND v_digit = 1 THEN
         -- 如果前面是空,或者前面是"零" (如 101 -> 一百零一十? 不,1010->一千零一十)
         -- 让我们看标准:
         -- 10: 十
         -- 11: 十一
         -- 110: 一百一十 (省略)
         -- 1010: 一千零一十 (省略)
         -- 结论:只要单位是十,数字是一,通常都省略"一",除非为了强调。
         -- 但要注意:如果前面刚补了"零",变成 "零一十" 就不对了,应该是 "零十"? 不对,是 "零一十"。
         -- 等等,1010 读作 "一千零一十"。这里 "一" 是保留的!
         -- 只有当 "一十" 位于 **该节的最前端** 且 **没有前导零** 时才省略?
         -- 重新校准:
         -- 10 -> 十 (省略)
         -- 110 -> 一百一十 (省略)
         -- 1010 -> 一千零一十 (保留 "一") -> 为什么?因为中间隔了零。
         -- 1100 -> 一千一百 (省略)
         
         -- 修正逻辑:如果前面紧挨着的是 "零",则保留 "一"。如果前面是其他单位或开头,则省略。
         IF v_res = '' OR SUBSTR(v_res, -1) NOT IN ('零') THEN
            v_res := v_res || v_unit; -- 省略 "一"
         ELSE
            v_res := v_res || v_char || v_unit; -- 保留 "一"
         END IF;
      ELSE
         v_res := v_res || v_char || v_unit;
      END IF;
      
    END IF;
  END LOOP;
  
  -- 清理末尾可能多余的零 (理论上循环逻辑不会产生,但以防万一)
  -- 如果结果是 "零" (输入0的情况已在开头处理),这里不需要动
  
  RETURN v_res;
END;

Sql 测试:

sql 复制代码
SELECT 
  num_val, 
  F_NUM_TO_CHN(num_val) as result
FROM (
  SELECT 1 AS num_val FROM DUAL UNION ALL
  SELECT 10 FROM DUAL UNION ALL
  SELECT 11 FROM DUAL UNION ALL
  SELECT 100 FROM DUAL UNION ALL
  SELECT 101 FROM DUAL UNION ALL
  SELECT 1000 FROM DUAL UNION ALL
  SELECT 1001 FROM DUAL UNION ALL  -- 重点测试:应为一千零一
  SELECT 1010 FROM DUAL UNION ALL  -- 重点测试:应为一千零一十
  SELECT 1011 FROM DUAL UNION ALL
  SELECT 1100 FROM DUAL UNION ALL
  SELECT 10000 FROM DUAL UNION ALL
  SELECT 10001 FROM DUAL UNION ALL -- 重点测试:应为一万零一
  SELECT 10010 FROM DUAL UNION ALL -- 重点测试:应为一万零一十
  SELECT 12345 FROM DUAL
);

Sql 结果:

结果合理。

相关推荐
寒风暖哥2 小时前
Oracle视图查询返回空数据集的分析
oracle·c#
2501_924952692 小时前
自动化机器学习(AutoML)库TPOT使用指南
jvm·数据库·python
诗酒当趁年华2 小时前
langchain核心组件1-智能体
数据库·langchain
流星白龙2 小时前
【MySQL】9.MySQL内置函数
android·数据库·mysql
原来是猿3 小时前
MySQL 在 Centos 7环境安装
数据库·mysql·centos
路小雨~3 小时前
Milvus 向量数据库的官方文档笔记
数据库·学习·milvus
老衲提灯找美女3 小时前
数据库约束
数据库
卷Java3 小时前
Python字典:键值对、get()方法、defaultdict,附通讯录实战
开发语言·数据库·python
wanhengidc3 小时前
跨境云手机适用于哪些场景
大数据·运维·服务器·数据库·科技·智能手机
Bdygsl4 小时前
MySQL(6)—— 视图
数据库·mysql