IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> ORACLE DATABASE 深度优先搜索的实例 -> 正文阅读

[大数据]ORACLE DATABASE 深度优先搜索的实例

我们经常会遇到这样的场景,知道程序的入口A,然后A调用XX,XX调用XXX……,最后XN调用D,我们只知道A和D,想要找到这个调用链,比如

A-B-C-D

但是很多情况不是像这样的链表或者树,而是调用很复杂的图。如下图

?

?为了模拟这个调用链,我们可以创建5个测试包。

CREATE OR REPLACE PACKAGE cux_test_d IS
  PROCEDURE test;
END cux_test_d;

CREATE OR REPLACE PACKAGE BODY cux_test_d IS

  PROCEDURE test IS
  BEGIN
    NULL;
  END;
END cux_test_d;
CREATE OR REPLACE PACKAGE cux_test_c IS
  PROCEDURE test;
END cux_test_c;

CREATE OR REPLACE PACKAGE BODY cux_test_c IS

  PROCEDURE test IS
  BEGIN
   cux_test_d.test;
  END;
END cux_test_c;
CREATE OR REPLACE PACKAGE cux_test_b IS
  PROCEDURE test;
END cux_test_b;

CREATE OR REPLACE PACKAGE BODY cux_test_b IS

  PROCEDURE test IS
  BEGIN
   cux_test_c.test;
  END;
END cux_test_b;
CREATE OR REPLACE PACKAGE cux_test_e IS
  PROCEDURE test;
END cux_test_e;

CREATE OR REPLACE PACKAGE BODY cux_test_e IS

  PROCEDURE test IS
  BEGIN
   cux_test_d.test;
  END;
END cux_test_e;
CREATE OR REPLACE PACKAGE cux_test_a IS
  PROCEDURE test_1;
  PROCEDURE test_2;
END cux_test_a;

CREATE OR REPLACE PACKAGE BODY cux_test_a IS

  PROCEDURE test_1 IS
  BEGIN
   cux_test_e.test;
  END;
  
  PROCEDURE test_2 IS
  BEGIN
   cux_test_b.test;
  END;
END cux_test_a;

而这个依赖关系,用视图all_dependencies提现,如下:

(可能存在更好的调用视图,我只找到了这个)

?这里有两点需要注意的地方

1.REFERENCED_NAME具有索引,而NAME不具有

2.Package Body CUX_TEST_C 调用了Package CUX_TEST_D,但是CUX_TEST_D的调用链仍旧是 Package Body调用其它,所以我们在处理的时候,需要把Package和Package Body当成同一个东西,我这里是都当成package 处理

而针对第一点,需要将我们的图变成下列形式(否则,用NAME查找下一个REFERENCED_NAME,没有索引了,所有查询都是全表扫描)

?因为从D找到C比较容易,而C找D比较难

仿照JavaScript数据结构-7-2图深度优先搜索_香辣素毛肚的博客-CSDN博客

在我们编写递归的深度优先搜索方法,我们需要创建一个栈的数据结构

先创建一个数组对象

 create or replace type cux_items_type  is table of varchar2(4000); 

然后编写我们的栈对象

CREATE OR REPLACE TYPE cux_stack_type AS OBJECT
(
  top   NUMBER,
  items cux_items_type,
  MEMBER PROCEDURE init(SELF IN OUT cux_stack_type),
  MEMBER FUNCTION is_empty RETURN BOOLEAN,
  MEMBER PROCEDURE push(SELF    IN OUT cux_stack_type
                       ,p_value IN VARCHAR2),
  MEMBER FUNCTION pop(SELF    IN OUT cux_stack_type
                     ,p_value OUT VARCHAR2) RETURN BOOLEAN

)
CREATE OR REPLACE TYPE BODY cux_stack_type IS


  MEMBER PROCEDURE init(SELF IN OUT cux_stack_type) IS
  BEGIN
    self.top   := 0;
    self.items := cux_items_type();
  END;

  MEMBER FUNCTION is_empty RETURN BOOLEAN IS
  BEGIN
    --PLSQL数组下标为1开始
    IF self.top = 0
    THEN
      RETURN TRUE;
    ELSE
      RETURN FALSE;
    END IF;
  END is_empty;

  MEMBER PROCEDURE push(SELF    IN OUT cux_stack_type
                       ,p_value IN VARCHAR2) IS
  
  BEGIN
    --可变长数组,不用考虑栈满
    self.top := self.top + 1;
    self.items.extend;
    self.items(self.top) := p_value;
  
  END push;

  MEMBER FUNCTION pop(SELF    IN OUT cux_stack_type
                     ,p_value OUT VARCHAR2) RETURN BOOLEAN IS
  BEGIN
    IF self.top = 0
    THEN
      RETURN FALSE; --栈空
    
    ELSE
    
      p_value  := self.items(self.top);
      self.top := self.top - 1;
      RETURN TRUE;
    
    END IF;
  
  END pop;
END;

测试一下

DECLARE
  stack     cux_stack_type;
  l_boolean BOOLEAN;
  l_value   VARCHAR2(240);
BEGIN
  stack := NEW cux_stack_type(0
                             ,NULL);

  stack.init;
  stack.push(1);
  stack.push(2);
  stack.push(3);
  l_boolean := stack.pop(l_value);
  dbms_output.put_line(l_value);
  l_boolean := stack.pop(l_value);
  dbms_output.put_line(l_value);
  l_boolean := stack.pop(l_value);
  dbms_output.put_line(l_value);
  l_boolean := stack.pop(l_value);
  dbms_output.put_line(l_value);
  IF NOT l_boolean
  THEN
    dbms_output.put_line('栈空');
  END IF;
END;

?现在可以开始DFS遍历了

创建视图

CREATE OR REPLACE VIEW CUX_ALL_DEPENDENCIES_V AS
SELECT "OWNER"
       ,"NAME"
       ,"TYPE"
       ,"REFERENCED_OWNER"
       ,"REFERENCED_NAME"
       ,"REFERENCED_TYPE"
       ,"REFERENCED_LINK_NAME"
       ,"DEPENDENCY_TYPE"
  FROM (SELECT owner
              ,NAME
              ,CASE
                 WHEN TYPE IN ('PACKAGE BODY'
                              ,'PACKAGE') THEN
                  'PACKAGE'
                 ELSE
                  TYPE
               END TYPE
              ,referenced_owner
              ,referenced_name
              ,CASE
                 WHEN referenced_type IN ('PACKAGE BODY'
                                         ,'PACKAGE') THEN
                  'PACKAGE'
                 ELSE
                  referenced_type
               END referenced_type
               
              ,referenced_link_name
              ,dependency_type
          FROM all_dependencies)
 WHERE 1 = 1
   AND NOT (referenced_name = NAME AND referenced_type = TYPE AND
        referenced_owner = owner);

使package body=package

创建程序包

CREATE OR REPLACE PACKAGE cux_util_pub IS
  TYPE stacks_tbl IS TABLE OF cux_stack_type INDEX BY BINARY_INTEGER;
  PROCEDURE find_dependencies(p_start_name IN VARCHAR2
                             ,p_start_type IN VARCHAR2
                             ,p_start_owner IN VARCHAR2
                             ,p_end_name IN VARCHAR2
                             ,p_end_type IN VARCHAR2
                             ,p_end_owner IN VARCHAR2
                             ,p_status_code OUT VARCHAR2
                             ,p_msg        OUT VARCHAR2
                             ,p_stacks_tbl OUT stacks_tbl);
  PROCEDURE find_all_path(p_start_object_id IN NUMBER
                         ,p_end_object_id IN NUMBER
                         ,p_stack    IN OUT NOCOPY cux_stack_type
                         ,d            IN OUT NUMBER
                         ,p_stacks_tbl IN OUT NOCOPY stacks_tbl
                          );
END cux_util_pub;
CREATE OR REPLACE PACKAGE BODY cux_util_pub IS
  --采用递归形式的深度优先搜索,可能会导致栈溢出,后期优化
  PROCEDURE find_dependencies(p_start_name  IN VARCHAR2
                             ,p_start_type  IN VARCHAR2
                             ,p_start_owner IN VARCHAR2
                             ,p_end_name    IN VARCHAR2
                             ,p_end_type    IN VARCHAR2
                             ,p_end_owner   IN VARCHAR2
                             ,p_status_code OUT VARCHAR2
                             ,p_msg         OUT VARCHAR2
                             ,p_stacks_tbl  OUT stacks_tbl) IS
    l_start_object_id NUMBER;
    l_end_object_id   NUMBER;
  --  l_path_tbl        path_tbl;
    d                 NUMBER := -1;
    l_start_type      VARCHAR2(240);
    l_end_type        VARCHAR2(240);
    l_stack           cux_stack_type;
  BEGIN
    --g_paths_tbl.delete;
    l_stack := NEW cux_stack_type(0
                                 ,NULL);
  
    l_stack.init;
    l_start_type:=p_start_type;
    l_end_type:=p_end_type;
    IF p_start_type LIKE 'PACKAGE%'
    THEN
      l_start_type := 'PACKAGE';
    END IF;
  
    IF p_end_type LIKE 'PACKAGE%'
    THEN
      l_end_type := 'PACKAGE';
    END IF;
  
    BEGIN
      SELECT d.object_id
        INTO l_start_object_id
        FROM sys.dba_objects d
       WHERE d.object_name = p_start_name
         AND d.object_type = l_start_type
         AND d.owner = p_start_owner;
    EXCEPTION
      WHEN OTHERS THEN
        p_status_code := 'E';
        p_msg         := '参数错误';
        return;
    END;
    BEGIN
      SELECT d.object_id
        INTO l_end_object_id
        FROM dba_objects d
       WHERE d.object_name = p_end_name
         AND d.object_type = l_end_type
         AND d.owner = p_end_owner;
    EXCEPTION
      WHEN OTHERS THEN
        p_status_code := 'E';
        p_msg         := '参数错误';
        return;
    END;
    find_all_path(p_start_object_id => l_start_object_id
                 ,p_end_object_id   => l_end_object_id
                 ,p_stack           => l_stack
                 ,d                 => d
                 ,p_stacks_tbl      => p_stacks_tbl);
    p_status_code := 'S';
  END find_dependencies;
  --获取该顶点第一个边表
  FUNCTION get_first_starc(p_object_id IN NUMBER) RETURN NUMBER IS
    l_start_object_name  VARCHAR2(240);
    l_start_object_type  VARCHAR2(240);
    l_start_object_owner VARCHAR2(240);
    l_next_object_name   VARCHAR2(240);
    l_next_object_type   VARCHAR2(240);
    l_next_object_owner  VARCHAR2(240);
    l_object_id          NUMBER;
  BEGIN
    SELECT d.object_name
          ,d.object_type
          ,d.owner
      INTO l_start_object_name
          ,l_start_object_type
          ,l_start_object_owner
      FROM dba_objects d
     WHERE d.object_id = p_object_id;
    /*    IF l_start_object_type LIKE 'PACKAGE' || '%'
    THEN
      --忽略package和package body区别
      l_start_object_type := 'PACKAGE';
    END IF;*/
    --没有链表,只能使用排序找下一个
  
    BEGIN
      SELECT NAME
            ,owner
            ,TYPE
        INTO l_next_object_name
            ,l_next_object_owner
            ,l_next_object_type
        FROM (SELECT a.name
                    ,a.owner
                    ,a.type
                    ,row_number() over(PARTITION BY 1 ORDER BY a.owner, a.name, a.type) row_number
                FROM cux_all_dependencies_v a
               WHERE a.referenced_owner = l_start_object_owner
                 AND a.referenced_name = l_start_object_name
                 AND a.referenced_type LIKE l_start_object_type)
       WHERE row_number = 1;
    EXCEPTION
      WHEN OTHERS THEN
        RETURN NULL;
    END;
  
    /*    IF l_next_object_type LIKE 'PACKAGE' || '%'
        THEN
          --忽略package和package body区别
          l_next_object_type := 'PACKAGE';
        END IF;
      
    */
    SELECT d.object_id
      INTO l_object_id
      FROM dba_objects d
     WHERE d.owner = l_next_object_owner
       AND d.object_name = l_next_object_name
       AND d.object_type = l_next_object_type;
    RETURN l_object_id;
  END get_first_starc;
  FUNCTION get_next_starc(p_parent_object_id IN NUMBER
                         ,p_son_object_id    IN NUMBER) RETURN NUMBER IS
    l_start_object_name  VARCHAR2(240);
    l_start_object_type  VARCHAR2(240);
    l_start_object_owner VARCHAR2(240);
    l_next_object_name   VARCHAR2(240);
    l_next_object_type   VARCHAR2(240);
    l_next_object_owner  VARCHAR2(240);
    l_object_id          NUMBER;
    l_son_object_name    VARCHAR2(240);
    l_son_object_type    VARCHAR2(240);
    l_son_object_owner   VARCHAR2(240);
    l_row_number         NUMBER;
  BEGIN
    SELECT d.object_name
          ,d.object_type
          ,d.owner
      INTO l_start_object_name
          ,l_start_object_type
          ,l_start_object_owner
      FROM dba_objects d
     WHERE d.object_id = p_parent_object_id;
    /*  IF l_start_object_type LIKE 'PACKAGE' || '%'
    THEN
      --忽略package和package body区别
      l_start_object_type := 'PACKAGE';
    END IF;*/
  
    SELECT d.object_name
          ,d.object_type
          ,d.owner
      INTO l_son_object_name
          ,l_son_object_type
          ,l_son_object_owner
      FROM dba_objects d
     WHERE d.object_id = p_son_object_id;
  
    BEGIN
      SELECT row_number
        INTO l_row_number
        FROM (SELECT a.name
                    ,a.owner
                    ,a.type
                    ,row_number() over(PARTITION BY 1 ORDER BY a.owner, a.name, a.type) row_number
                FROM cux_all_dependencies_v a
               WHERE a.referenced_owner = l_start_object_owner
                 AND a.referenced_name = l_start_object_name
                 AND a.referenced_type = l_start_object_type)
       WHERE NAME = l_son_object_name
         AND owner = l_son_object_owner
         AND TYPE = l_son_object_type;
    EXCEPTION
      WHEN OTHERS THEN
        RETURN NULL;
    END;
    BEGIN
      SELECT NAME
            ,owner
            ,TYPE
        INTO l_next_object_name
            ,l_next_object_owner
            ,l_next_object_type
        FROM (SELECT a.name
                    ,a.owner
                    ,a.type
                    ,row_number() over(PARTITION BY 1 ORDER BY a.owner, a.name, a.type) row_number
                FROM cux_all_dependencies_v a
               WHERE a.referenced_owner = l_start_object_owner
                 AND a.referenced_name = l_start_object_name
                 AND a.referenced_type = l_start_object_type)
       WHERE row_number = l_row_number + 1;
    EXCEPTION
      WHEN OTHERS THEN
        RETURN NULL;
    END;
    SELECT d.object_id
      INTO l_object_id
      FROM dba_objects d
     WHERE d.owner = l_next_object_owner
       AND d.object_name = l_next_object_name
       AND d.object_type = l_next_object_type;
    RETURN l_object_id;
  END get_next_starc;


  PROCEDURE print_stack_name(p_stack IN cux_stack_type) IS
    l_info VARCHAR2(4000);
  BEGIN
  
    IF p_stack.top <> 0
    THEN
    
      FOR i IN 1..p_stack.top
      LOOP
      
        BEGIN
          SELECT d.owner || '.' || d.object_name || '(' || d.object_type || ')'
            INTO l_info
            FROM dba_objects d
           WHERE d.object_id = p_stack.items(i);
          dbms_output.put(l_info || '->');
        EXCEPTION
          WHEN OTHERS THEN
            NULL;
        END;
      
      END LOOP;
      dbms_output.new_line;
    
    END IF;
  END print_stack_name;
  --BFS
  --package和package body 统一取package的 object_id
  PROCEDURE find_all_path(p_start_object_id IN NUMBER
                         ,p_end_object_id   IN NUMBER
                         ,p_stack           IN OUT NOCOPY cux_stack_type
                         ,d                 IN OUT NUMBER
                         ,p_stacks_tbl      IN OUT NOCOPY stacks_tbl) IS
    -- d NUMBER := -1; --路径长度
    -- l_path_tbl path_tbl; --路径数组
    TYPE hash_map IS TABLE OF NUMBER INDEX BY VARCHAR2(240);
    l_visited hash_map; --类似键值对,该节点是否已被访问
    --  
    -- l_paths_tbl paths_tbl; --记录所有类型数组
    p                NUMBER;
    w                NUMBER;
    l_current_index  NUMBER;
    l_visited_result NUMBER;
    l_pop_boolean    BOOLEAN;
    l_value          VARCHAR2(4000);
  BEGIN
    d := d + 1;
  --  p_path_tbl(d) := p_start_object_id;
    --入栈
    p_stack.push(p_start_object_id);
  
    l_visited(p_start_object_id) := 1;
    IF p_start_object_id = p_end_object_id
    THEN
      --当p_start_object_id_n=p_end_object_id 则表示找到了A-B路径
      --写入二维数组
      --数组实现不了,只能写入栈里了
   --   g_paths_tbl(g_paths_tbl.count) := p_path_tbl;
   --    print_object_name(p_path_tbl => p_path_tbl);
      --出栈
      p_stacks_tbl(p_stacks_tbl.count) := p_stack;
      l_pop_boolean := p_stack.pop(l_value);
      print_stack_name(p_stack=>p_stack);
      --p_path_tbl.DELETE;
    END IF;
    --p指向p_start_object_id的第一个临点
    p := get_first_starc(p_object_id => p_start_object_id);
    WHILE (p IS NOT NULL)
    LOOP
      w := p;
      BEGIN
        l_visited_result := l_visited(w);
      EXCEPTION
        WHEN OTHERS THEN
          l_visited_result := 0;
      END;
      IF l_visited_result = 0
      THEN
        --该节点未访问过
        find_all_path(w
                     ,p_end_object_id
                     ,p_stack
                     ,d
                     ,p_stacks_tbl);
        p := get_next_starc(p_start_object_id
                           ,p);
      END IF;
    END LOOP;
    --出栈
    l_pop_boolean := p_stack.pop(l_value);
  
    l_visited(p_start_object_id) := 0;
  END find_all_path;
END cux_util_pub;

?测试脚本

DECLARE
  p_status_code VARCHAR2(240);
  p_msg         VARCHAR2(240);
  p_stacks_tbl cux_util_pub.stacks_tbl;
BEGIN
  cux_util_pub.find_dependencies(p_start_name  => 'CUX_TEST_D'
                                    ,p_start_type  => 'PACKAGE'
                                    ,p_start_owner => 'APPS'
                                    ,p_end_name    => 'CUX_TEST_A'
                                    ,p_end_type    => 'PACKAGE'
                                    ,p_end_owner   => 'APPS'
                                    ,p_status_code => p_status_code
                                    ,p_msg         => p_msg
                                    ,p_stacks_tbl=>p_stacks_tbl);
  dbms_output.put_line(p_status_code);
  dbms_output.put_line(p_msg);
END;

结果如下

?上图的p_stacks_tbl变量,存储了所有路径的栈,将栈里的元素一个个弹出,就是正确的A-B-C-D顺序

该算法无法处理有向带环图,比如下面的例子

后期进行优化

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-09-13 09:20:26  更:2021-09-13 09:22:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:53:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码