Oracle数据库数据编程SQL<递归函数详解>

news2025/4/1 23:22:01

递归函数是一种在函数体内直接或间接调用自身的函数。这种函数通过将复杂问题分解为更小的相同问题来解决特定类型的编程任务。

目录

一、递归函数基本概念

1. 递归定义

2. 递归工作原理

 二、递归函数示例

1. 经典阶乘函数

2. 斐波那契数列

3. 计算数字位数

三、递归查询应用

1. 递归WITH子句(公用表表达式)

2. 层次结构查询(员工-经理关系)

 四、递归函数设计要点

 五、递归与迭代对比

1. 递归与迭代对比

2. 迭代实现阶乘(对比) 

六、递归函数的高级应用

1. 遍历树形结构

2. 解决汉诺塔问题

七、递归的局限性及优化

1. Oracle递归深度限制

2. 尾递归优化

3. 记忆化(Memoization)优化


一、递归函数基本概念

1. 递归定义

递归函数包含两个关键部分:

  • 基准条件(Base Case):确定递归何时结束,避免无限循环

  • 递归条件(Recursive Case):将问题分解为更小的子问题并调用自身

2. 递归工作原理

  • (1)函数检查是否满足基准条件
  • (2)如果满足,返回基准值
  • (3)如果不满足,将问题分解并调用自身处理更小的子问题
  • (4)最终将所有子问题的结果组合成最终解

 二、递归函数示例

1. 经典阶乘函数

CREATE OR REPLACE FUNCTION factorial(
    p_num IN NUMBER
) RETURN NUMBER
IS
BEGIN
    -- 基准条件:0!和1!都等于1
    IF p_num <= 1 THEN
        RETURN 1;
    ELSE
        -- 递归条件:n! = n * (n-1)!
        RETURN p_num * factorial(p_num - 1);
    END IF;
END factorial;
/

-- 调用示例
SELECT factorial(5) FROM dual;  -- 返回120 (5! = 5×4×3×2×1 = 120)

2. 斐波那契数列

CREATE OR REPLACE FUNCTION fibonacci(
    p_num IN NUMBER
) RETURN NUMBER
IS
BEGIN
    -- 基准条件
    IF p_num = 0 THEN
        RETURN 0;
    ELSIF p_num = 1 THEN
        RETURN 1;
    ELSE
        -- 递归条件:F(n) = F(n-1) + F(n-2)
        RETURN fibonacci(p_num - 1) + fibonacci(p_num - 2);
    END IF;
END fibonacci;
/

-- 调用示例
SELECT fibonacci(10) FROM dual;  -- 返回55 (斐波那契数列第10项)

3. 计算数字位数

CREATE OR REPLACE FUNCTION count_digits(
    p_num IN NUMBER
) RETURN NUMBER
IS
BEGIN
    -- 基准条件:单个数字
    IF p_num BETWEEN -9 AND 9 THEN
        RETURN 1;
    ELSE
        -- 递归条件:去掉最后一位后计数+1
        RETURN 1 + count_digits(TRUNC(p_num/10));
    END IF;
END count_digits;
/

-- 调用示例
SELECT count_digits(12345) FROM dual;  -- 返回5

三、递归查询应用

 Oracle中的递归不仅限于PL/SQL函数,还可以用于SQL递归查询:

1. 递归WITH子句(公用表表达式)

-- 计算1到10的和
WITH recursive_sum(n, total) AS (
    -- 基准查询
    SELECT 1, 1 FROM dual
    
    UNION ALL
    
    -- 递归查询
    SELECT n+1, total+(n+1)
    FROM recursive_sum
    WHERE n < 10
)
SELECT * FROM recursive_sum;

/*
结果:
N  TOTAL
1     1
2     3
3     6
4    10
5    15
6    21
7    28
8    36
9    45
10   55
*/

2. 层次结构查询(员工-经理关系)

-- 查找员工的管理链
WITH emp_hierarchy AS (
    -- 基准查询:从指定员工开始
    SELECT employee_id, last_name, manager_id, 1 AS level
    FROM employees
    WHERE employee_id = 110
    
    UNION ALL
    
    -- 递归查询:向上查找经理
    SELECT e.employee_id, e.last_name, e.manager_id, h.level + 1
    FROM employees e
    JOIN emp_hierarchy h ON e.employee_id = h.manager_id
)
SELECT LPAD(' ', 2*(level-1)) || last_name AS management_chain
FROM emp_hierarchy
ORDER BY level DESC;

/*
结果:
MANAGEMENT_CHAIN
    King
      Higgins
        Gietz
*/

 四、递归函数设计要点

  1. 必须存在基准条件:确保递归能够终止

  2. 每次递归应使问题更接近基准条件:避免无限递归

  3. 注意递归深度:Oracle有递归深度限制(默认约1000层)

  4. 性能考虑:递归可能比迭代解决方案效率低

  5. 内存使用:深层递归会消耗大量栈空间

 五、递归与迭代对比

1. 递归与迭代对比

特性递归迭代
代码简洁性更简洁通常更冗长
内存使用使用调用栈,可能栈溢出通常更高效
性能函数调用开销大通常更快
适用场景问题自然递归时大多数情况
可读性数学定义清晰时更易读流程更直观

2. 迭代实现阶乘(对比) 

CREATE OR REPLACE FUNCTION factorial_iter(
    p_num IN NUMBER
) RETURN NUMBER
IS
    v_result NUMBER := 1;
BEGIN
    FOR i IN 1..p_num LOOP
        v_result := v_result * i;
    END LOOP;
    
    RETURN v_result;
END factorial_iter;
/

六、递归函数的高级应用

1. 遍历树形结构

CREATE OR REPLACE PROCEDURE print_folder_tree(
    p_folder_id IN NUMBER,
    p_level IN NUMBER DEFAULT 0
) IS
    CURSOR folder_cur IS
        SELECT folder_id, folder_name
        FROM folders
        WHERE parent_id = p_folder_id;
BEGIN
    -- 打印当前文件夹(带缩进)
    DBMS_OUTPUT.PUT_LINE(LPAD(' ', p_level*2) || 'Folder: ' || p_folder_id);
    
    -- 递归处理子文件夹
    FOR f_rec IN folder_cur LOOP
        print_folder_tree(f_rec.folder_id, p_level + 1);
    END LOOP;
END print_folder_tree;
/

2. 解决汉诺塔问题

CREATE OR REPLACE PROCEDURE hanoi(
    p_disks IN NUMBER,
    p_from IN VARCHAR2,
    p_to IN VARCHAR2,
    p_aux IN VARCHAR2
) IS
BEGIN
    IF p_disks = 1 THEN
        DBMS_OUTPUT.PUT_LINE('移动盘子 1 从 ' || p_from || ' 到 ' || p_to);
    ELSE
        -- 将n-1个盘子从起始柱移动到辅助柱
        hanoi(p_disks - 1, p_from, p_aux, p_to);
        
        -- 移动第n个盘子到目标柱
        DBMS_OUTPUT.PUT_LINE('移动盘子 ' || p_disks || ' 从 ' || p_from || ' 到 ' || p_to);
        
        -- 将n-1个盘子从辅助柱移动到目标柱
        hanoi(p_disks - 1, p_aux, p_to, p_from);
    END IF;
END hanoi;
/

-- 调用示例
EXEC hanoi(3, 'A', 'C', 'B');

七、递归的局限性及优化

1. Oracle递归深度限制

 Oracle默认递归深度限制约为1000层,可通过以下方式调整:

ALTER SESSION SET recursive_with_clause = 10000;  -- 增加递归WITH限制

对于PL/SQL递归函数,

可通过修改_recursive_with_balancing等隐藏参数调整限制(需DBA权限)

2. 尾递归优化

Oracle PL/SQL不自动执行尾递归优化,但可手动重构:

-- 普通递归
CREATE OR REPLACE FUNCTION sum_to_n(
    p_n IN NUMBER
) RETURN NUMBER
IS
BEGIN
    IF p_n <= 1 THEN
        RETURN 1;
    ELSE
        RETURN p_n + sum_to_n(p_n - 1);
    END IF;
END;
/

-- 尾递归形式(Oracle不会优化,但其他语言可能优化)
CREATE OR REPLACE FUNCTION sum_to_n_tail(
    p_n IN NUMBER,
    p_acc IN NUMBER DEFAULT 0
) RETURN NUMBER
IS
BEGIN
    IF p_n <= 0 THEN
        RETURN p_acc;
    ELSE
        RETURN sum_to_n_tail(p_n - 1, p_acc + p_n);
    END IF;
END;
/

3. 记忆化(Memoization)优化

缓存已计算结果,避免重复计算:

CREATE OR REPLACE PACKAGE fib_pkg AS
    TYPE num_array IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
    g_fib_cache num_array;
    
    FUNCTION fibonacci(p_n NUMBER) RETURN NUMBER;
END fib_pkg;
/

CREATE OR REPLACE PACKAGE BODY fib_pkg AS
    FUNCTION fibonacci(p_n NUMBER) RETURN NUMBER
    IS
    BEGIN
        -- 基准条件
        IF p_n = 0 THEN
            RETURN 0;
        ELSIF p_n = 1 THEN
            RETURN 1;
        END IF;
        
        -- 检查缓存
        IF g_fib_cache.EXISTS(p_n) THEN
            RETURN g_fib_cache(p_n);
        END IF;
        
        -- 计算并缓存结果
        g_fib_cache(p_n) := fibonacci(p_n - 1) + fibonacci(p_n - 2);
        RETURN g_fib_cache(p_n);
    END;
END fib_pkg;
/

递归函数是解决分治问题和层次结构处理的强大工具,但需要谨慎设计以避免性能问题和栈溢出错误。在Oracle环境中,对于深度递归问题,有时使用迭代方法或递归WITH子句可能是更好的选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2325201.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis-08.Redis常用命令-有序集合操作命令

一.有序集合操作命令 ZADD key score 1 member1 [score2 member2]&#xff1a; zadd zset 10.0 a 10.5 b ZRANGE key start stop [WITHSCORES]: zrange zset 0 -1 为何顺序为a&#xff0c;c&#xff0c;b&#xff1f; 因为 zrange zset 0 -1 withscores zrange key start …

LLaMA-Factory使用实战

LLaMA-Factory使用实战 项目介绍 项目地址&#xff1a;https://github.com/hiyouga/LLaMA-Factory 中文文档&#xff1a;安装 - LLaMA Factory 快速开始文档&#xff1a;https://zhuanlan.zhihu.com/p/695287607&#xff08;推荐参考&#xff09; 远程服务器通过本地代理加…

读一本书,骑行万里路:与维乐 Angel Rise+骑行看世界

最近读到了一本名为《自行车改变的世界&#xff1a;女性骑行的历史》的书&#xff0c;才发现原来女性的骑行自由来得并不轻易&#xff0c;激励着每一位女性勇敢地踏上骑行之路。而我一直在使用的维乐坐垫品牌&#xff0c;除了产品专业之外&#xff0c;也一直都非常关注女性骑行…

【大模型】SpringBoot整合LangChain4j实现RAG检索实战详解

目录 一、前言 二、LangChain4j 介绍 2.1 什么是LangChain4j 2.2 LangChain4j 主要特点 2.3 Langchain4j 核心组件 三、RAG介绍 3.1 什么是RAG 3.2 RAG工作流程 3.2.1 补充说明 3.3 Embedding模型 3.3.1 RAG实际使用步骤 3.3.2 什么是Embedding 3.3.3 Embedding 技…

流动的梦境:GPT-4o 的自回归图像生成深度解析

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

北大人工智能研究院朱松纯:“中国的AI叙事” 存在认知偏差

3月29日&#xff0c;在2025中关村论坛通用人工智能论坛上&#xff0c;北京通用人工智能学院院长&#xff0c;北京大学人工智能研究院、智能学院院长朱松纯表示&#xff0c;目前&#xff0c;行业对AI的讨论几乎被大模型能力所占据&#xff0c;而基础学科、原始创新与智能本质的研…

习题1.26

解释题&#xff0c;说简单也简单&#xff0c;难在如何表达清楚。 首先解释下代码的变化 (defn expmod[base exp m](cond ( exp 0) 1(even? exp) (mod (square (expmod base (/ exp 2) m)) m):else (mod (* base (expmod base (- exp 1) m)) m)))(defn expmod[base exp m](co…

FPGA调试笔记

XILINX SSTL属性电平报错 错误如下&#xff1a; [DRC BIVRU-1] Bank IO standard Vref utilization: Bank 33 contains ports that use a reference voltage. In order to use such standards in a bank that is not configured to use INTERNAL_VREF, the banks VREF pin mu…

基于Java(SSM)+Mysql实现移动大厅业务办理(增删改查)

基于 SSM 框架的移动业务大厅 数据库需要自行创建&#xff01; 一、 整体基本实现情况 对本学期的 Java 作业 1 的 SOSO 移动大厅进行改进&#xff0c; 基于 SSM、JSP、Maven、Tomcat、MySQL 等实现。 二、 实现详情 1、 工程结构图 2、 工程结构各部分实现 &#xff08;…

【字符设备驱动开发–IMX6ULL】(一)简介

【字符设备驱动开发–IMX6ULL】&#xff08;一&#xff09;简介 一、Linux驱动与裸机开发区别 1.裸机驱动开发回顾 ​ 1、底层&#xff0c;跟寄存器打交道&#xff0c;有些MCU提供了库。 spi.c&#xff1a;主机驱动&#xff08;换成任何一个设备之后只需要调用此文件里面的…

C++_STL之list篇

一、list的介绍 std::list是C标准模板库(STL)中的一个双向链表容器。与vector和deque不同&#xff0c;list不支持随机访问&#xff0c;但它在任何位置插入和删除元素都非常高效。 1.基本特性 (1)双向链表结构&#xff1a;每个元素都包含指向前驱和后继的指针 (2)非连续存储&…

Spring 声明式事务 万字详解(通俗易懂)

目录 Δ前言 一、声明式事务快速入门 1.为什么需要声明式事务&#xff1f; 2.定义&#xff1a; 3.应用实例&#xff1a; 二、声明式事务的传播机制 1.引出问题&#xff1a; 2.传播机制分类&#xff1a; 3.应用实例&#xff1a; 三、声明式事务的隔离机制 1.四种隔离级别&…

MySQL 当中的锁

MySQL 当中的锁 文章目录 MySQL 当中的锁MySQL 中有哪些主要类型的锁&#xff1f;请简要说明MySQL 的全局锁有什么用&#xff1f;MySQL 的表级锁有哪些&#xff1f;作用是什么&#xff1f;元数据锁&#xff08;MetaData Lock&#xff0c;MDL&#xff09;意向锁&#xff08;Inte…

[Linux]基础IO

基础IO C文件IO相关操作磁盘文件与内存文件inode&#xff08;index node&#xff09;硬链接与软连接硬链接软连接总结 动静态库静态库动态库总结 C文件IO相关操作 当前路径&#xff1a;进程运行的时候&#xff0c;所处的路径叫做当前路径 打开文件的时候&#xff0c;一定是进…

力扣刷题-热题100题-第27题(c++、python)

21. 合并两个有序链表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/merge-two-sorted-lists/description/?envTypestudy-plan-v2&envIdtop-100-liked 常规法 创建一个新链表&#xff0c;遍历list1与list2&#xff0c;将新链表指向list1与list2…

Vue3 其它API Teleport 传送门

Vue3 其它API Teleport 传送门 在定义一个模态框时&#xff0c;父组件的filter属性会影响子组件的position属性&#xff0c;导致模态框定位错误使用Teleport解决这个问题把模态框代码传送到body标签下

windows下安装sublime

sublime4 alpha 4098 版本 下载 可以根据待破解的版本选择下载 https://www.sublimetext.com/dev crack alpha4098 的licence 在----- BEGIN LICENSE ----- TwitterInc 200 User License EA7E-890007 1D77F72E 390CDD93 4DCBA022 FAF60790 61AA12C0 A37081C5 D0316412 4584D…

Java高级JVM知识点记录,内存结构,垃圾回收,类文件结构,类加载器

JVM是Java高级部分&#xff0c;深入理解程序的运行及原理&#xff0c;面试中也问的比较多。 JVM是Java程序运行的虚拟机环境&#xff0c;实现了“一次编写&#xff0c;到处运行”。它负责将字节码解释或编译为机器码&#xff0c;管理内存和资源&#xff0c;并提供运行时环境&a…

【STL】queue

q u e u e queue queue 是一种容器适配器&#xff0c;设计为先进先出&#xff08; F i r s t I n F i r s t O u t , F I F O First\ In\ First\ Out,\ FIFO First In First Out, FIFO&#xff09;的数据结构&#xff0c;有两个出口&#xff0c;将元素推入队列的操作称为 p u …

20250330-傅里叶级数专题之离散时间傅里叶变换(4/6)

4. 傅里叶级数专题之离散时间傅里叶变换 20250328-傅里叶级数专题之数学基础(0/6)-CSDN博客20250330-傅里叶级数专题之傅里叶级数(1/6)-CSDN博客20250330-傅里叶级数专题之傅里叶变换(2/6)-CSDN博客20250330-傅里叶级数专题之离散傅里叶级数(3/6)-CSDN博客20250330-傅里叶级数专…