最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MySQL遞歸查詢的幾種實(shí)現(xiàn)方法

 更新時(shí)間:2024年10月28日 10:57:11   作者:芝麻\n  
本文主要介紹了MySQL遞歸查詢的幾種實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

背景

相信大家在平時(shí)開發(fā)的時(shí)候都遇見過以下這種樹形數(shù)據(jù)

在這里插入圖片描述

這種樹形數(shù)據(jù)如何落庫應(yīng)該這里就不贅述了

核心就是使用額外一個(gè)字段parent_id保存父親節(jié)點(diǎn)的id,如下圖所示

在這里插入圖片描述

這里的classpath指的是當(dāng)前節(jié)點(diǎn)的路徑,后續(xù)說明其作用

現(xiàn)有需求如下:
1、查詢指定id的分類節(jié)點(diǎn)的所有子節(jié)點(diǎn)2、查詢指定id的分類節(jié)點(diǎn)的所有父節(jié)點(diǎn)3、查詢整棵分類樹,可指定最大層級(jí)

常規(guī)操作

常規(guī)操作就是直接在程序?qū)用婵刂七f歸,下面根據(jù)需求一 一演示代碼。

PS:基礎(chǔ)工程代碼就不演示了,工程項(xiàng)目代碼在評(píng)論區(qū)鏈接中獲取

查詢指定id的分類節(jié)點(diǎn)的所有子節(jié)點(diǎn)

NormalController

    /**
     * 返回指定nodeId的節(jié)點(diǎn)信息,包括所有孩子節(jié)點(diǎn)
     * @param nodeId
     * @return
     */
    @GetMapping("/childNodes/{nodeId}")
    public CategoryVO childNodes(@PathVariable("nodeId") Integer nodeId){
        return categoryService.normalChildNodes(nodeId);
    }

CategoryServiceImpl

    @Override
    public CategoryVO normalChildNodes(Integer nodeId) {
        // 查詢當(dāng)前節(jié)點(diǎn)信息
        Category category = getById(nodeId);
        return assembleChildren(category);
    }
    private CategoryVO assembleChildren(Category category) {
        // 組裝vo信息
        CategoryVO categoryVO = BeanUtil.copyProperties(category, CategoryVO.class);
        // 如果沒有子節(jié)點(diǎn)了,則退出遞歸
        List<Category> children = getChildren(category.getId());
        if (children == null || children.isEmpty()) {
            return categoryVO;
        }
        List<CategoryVO> childrenVOs = new ArrayList<>();
        for (Category child : children) {
            // 組裝每一個(gè)孩子節(jié)點(diǎn)
            CategoryVO cv = assembleChildren(child);
            // 將其加入到當(dāng)前層的孩子節(jié)點(diǎn)集合中
            childrenVOs.add(cv);
        }
        categoryVO.setChildren(childrenVOs);
        return categoryVO;
    }
    private List<Category> getChildren(int nodeId) {
        // 如果不存在父親節(jié)點(diǎn)為nodeId的,則說明nodeId并不存在子節(jié)點(diǎn)
        return lambdaQuery().eq(Category::getParentId,nodeId).list();
    }

查詢id為6的分類信息

在這里插入圖片描述

查詢指定id的分類節(jié)點(diǎn)的所有父節(jié)點(diǎn)

NormalController

    /**
     * 返回指定nodeId的節(jié)點(diǎn)父級(jí)集合,按照從下到上的順序
     * @param nodeId
     * @return
     */
    @GetMapping("/parentNodes/{nodeId}")
    public List<Category> parentNodes(@PathVariable("nodeId") Integer nodeId){
        return categoryService.normalParentNodes(nodeId);
    }

CategoryServiceImpl

    @Override
    public List<Category> normalParentNodes(Integer nodeId) {
        Category category = getById(nodeId);
        // 找到其所有的父親節(jié)點(diǎn)信息,即根據(jù)category的parentId一直查,直到查不到
        List<Category> parentCategories = new ArrayList<>();
        Category current = category;
        while (true) {
            Category parent = lambdaQuery().eq(Category::getId, current.getParentId()).one();
            if (parent == null) {
                break;
            }
            parentCategories.add(parent);
            current = parent;
        }
        return parentCategories;
    }

查詢id為12的父級(jí)分類信息

在這里插入圖片描述

查詢整棵分類樹,可指定最大層級(jí)

NormalController

    /**
     * 返回整棵分類樹,可設(shè)置最大層級(jí)
     * @param maxLevel
     * @return
     */
    @GetMapping("/treeCategory")
    public List<CategoryVO> treeCategory(@RequestParam(value = "maxLevel",required = false) Integer maxLevel){
        return categoryService.normalTreeCategory(maxLevel);
    }

CategoryServiceImpl

    @Override
    public List<CategoryVO> normalTreeCategory(Integer maxLevel) {
        // 虛擬根節(jié)點(diǎn)
        CategoryVO root = new CategoryVO();
        root.setId(-1);
        root.setName("ROOT");
        root.setClasspath("/");
        // 隊(duì)列,為了控制層級(jí)的
        Queue<CategoryVO> queue = new LinkedList<>();
        queue.offer(root);
        int level = 1;
        while (!queue.isEmpty()) {
            // 到達(dá)最大層級(jí)了
            if (maxLevel != null && maxLevel == level) {
                break;
            }
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                CategoryVO poll = queue.poll();
                if (poll == null) {
                    continue;
                }
                //得到當(dāng)前層級(jí)的所有孩子節(jié)點(diǎn)
                List<Category> children = getChildren(poll.getId());
                // 有孩子節(jié)點(diǎn)
                if (children != null && !children.isEmpty()) {
                    List<CategoryVO> childrenVOs = new ArrayList<>();
                    // 構(gòu)建孩子節(jié)點(diǎn)
                    for (Category child : children) {
                        CategoryVO cv = BeanUtil.copyProperties(child, CategoryVO.class);
                        childrenVOs.add(cv);
                        queue.offer(cv);
                    }
                    // 設(shè)置孩子節(jié)點(diǎn)
                    poll.setChildren(childrenVOs);
                }
            }
            // 層級(jí)自增
            level++;
        }
        // 返回虛擬節(jié)點(diǎn)的孩子節(jié)點(diǎn)
        return root.getChildren();
    }

查詢整棵分類樹

在這里插入圖片描述

在這里插入圖片描述

MySQL8新特性

MySQL8有一個(gè)新特性就是with共用表表達(dá)式,使用這個(gè)特性就可以在MySQL層面實(shí)現(xiàn)遞歸查詢。

我們先來看看從上至下的遞歸查詢的SQL語句,查詢id為1的節(jié)點(diǎn)的所有子節(jié)點(diǎn)

WITH recursive r as (
	-- 遞歸基:由此開始遞歸
	select id,parent_id,name from category where id = 1
	union ALL
	-- 遞歸步:關(guān)聯(lián)查詢
	select c.id,c.parent_id,c.name
	from category c inner join r 
	-- r作為父表,c作為子表,所以查詢條件是c的parent_id=r.id
	where r.id = c.parent_id
)
select id,parent_id,name from r

查詢結(jié)果如下圖所示

在這里插入圖片描述

舉一反三,則查詢id為12的所有父節(jié)點(diǎn)信息的就是從下至上的遞歸查詢,SQL如下所示

WITH recursive r as (
	-- 遞歸基:從id為12的開始
	select id,parent_id,name from category where id = 12
	union ALL
	-- 遞歸步
	select c.id,c.parent_id,c.name
	from category c inner join r 
	-- 因?yàn)槭菑南轮辽系牟?,所以c作為子表,r作為父表
	where r.parent_id = c.id
)
select id,parent_id,name from r

結(jié)果如下圖所示

在這里插入圖片描述

查詢指定id的分類節(jié)點(diǎn)的所有子節(jié)點(diǎn)

AdvancedController

    /**
     * 返回指定nodeId的節(jié)點(diǎn)信息,包括所有孩子節(jié)點(diǎn)
     * @param nodeId
     * @return
     */
    @GetMapping("/childNodes/{nodeId}")
    public CategoryVO childNodes(@PathVariable("nodeId") Integer nodeId){
        return categoryService.advancedChildNodes(nodeId);
    }

CategoryServiceImpl

    @Override
    public CategoryVO advancedChildNodes(Integer nodeId) {
        List<Category> categories = categoryMapper.advancedChildNodes(nodeId);
        List<CategoryVO> assemble = assemble(categories);
        // 這里一定是第一個(gè),因?yàn)閏ategories集合中的是id為nodeId和其子分類的信息,結(jié)果assemble組裝后,只會(huì)存在一個(gè)根節(jié)點(diǎn)
        return assemble.get(0);
    }
    // 組裝categories
    private List<CategoryVO> assemble(List<Category> categories){
        // 組裝categories
        CategoryVO root = new CategoryVO();
        root.setId(-1);
        root.setChildren(new ArrayList<>());
        Map<Integer,CategoryVO> categoryMap = new HashMap<>();
        categoryMap.put(-1, root);
        for (Category category : categories) {
            CategoryVO categoryVO = BeanUtil.copyProperties(category, CategoryVO.class);
            categoryVO.setChildren(new ArrayList<>());
            categoryMap.put(category.getId(), categoryVO);
        }
        for (Category category : categories) {
            // 得到自身節(jié)點(diǎn)
            CategoryVO categoryVO = categoryMap.get(category.getId());
            // 得到父親節(jié)點(diǎn)
            CategoryVO parent = categoryMap.get(category.getParentId());
            // 沒有父親節(jié)點(diǎn)(此情況只會(huì)在數(shù)據(jù)庫中最上層節(jié)點(diǎn)的父節(jié)點(diǎn)id不為-1的時(shí)候出現(xiàn))
            if (parent == null) {
                root.getChildren().add(categoryVO);
                continue;
            }
            parent.getChildren().add(categoryVO);
        }
        return root.getChildren();
    }

CategoryMapper

    <select id="advancedChildNodes" resultType="com.example.mysql8recursive.entity.Category">
        WITH recursive r as (select id, parent_id, name,classpath
                             from category
                             where id = #{nodeId}
                             union ALL
                             select c.id, c.parent_id, c.name,c.classpath
                             from category c
                                      inner join r
                             where r.id = c.parent_id)

        select id, parent_id, name, classpath
        from r
    </select>

查詢分類id為6的分類信息

在這里插入圖片描述

拓展

這里其實(shí)還有另一種利用mybatis的collection子查詢的寫法,一筆帶過

    <resultMap id="BaseResultMap" type="com.example.mysql8recursive.entity.Category">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="parentId" column="parent_id"/>
        <result property="classpath" column="classpath"/>
    </resultMap>
    <resultMap id="CategoryVOResultMap" type="com.example.mysql8recursive.vo.CategoryVO" extends="BaseResultMap">
        <collection property="children"
                    column="id"
                    ofType="com.example.mysql8recursive.vo.CategoryVO"
                    javaType="java.util.ArrayList"
                    select="advancedChildNodes"
        >
        </collection>
    </resultMap>

    <select id="advancedChildNodes" resultMap="CategoryVOResultMap">
        select * from category where parent_id = #{id}
    </select>

查詢指定id的分類節(jié)點(diǎn)的所有父節(jié)點(diǎn)

AdvancedController

    /**
     * 返回指定nodeId的節(jié)點(diǎn)父級(jí)集合,按照從下到上的順序
     * @param nodeId
     * @return
     */
    @GetMapping("/parentNodes/{nodeId}")
    public List<Category> parentNodes(@PathVariable("nodeId") Integer nodeId){
        return categoryService.advancedParentNodes(nodeId);
    }

CategorySericeImpl

    @Override
    public List<Category> advancedParentNodes(Integer nodeId) {
        return categoryMapper.advancedParentNodes(nodeId);
    }

CategoryMapper

    <select id="advancedParentNodes" resultType="com.example.mysql8recursive.entity.Category">
        WITH recursive r as (select id, parent_id, name, classpath
                             from category
                             where id = #{nodeId}
                             union ALL
                             select c.id, c.parent_id, c.name, c.classpath
                             from category c
                                      inner join r
                             where r.parent_id = c.id)

        select id, parent_id, name, classpath
        from r
    </select>

查詢分類id為12的所有父級(jí)分類信息

在這里插入圖片描述

查詢整棵分類樹

AdvancedController

    /**
     * 返回整棵分類樹
     * @return
     */
    @GetMapping("/treeCategory")
    public List<CategoryVO> treeCategory(){
        return categoryService.advancedTreeCategory();
    }

CategoryServiceImpl

    @Override
    public List<CategoryVO> advancedTreeCategory() {
        return assemble(list());
    }

查詢整棵分類樹

在這里插入圖片描述

在這里插入圖片描述

到此這篇關(guān)于MySQL遞歸查詢的幾種實(shí)現(xiàn)方法的文章就介紹到這了,更多相關(guān)MySQL遞歸查詢內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • Mysql中LEFT JOIN和JOIN查詢區(qū)別及原理詳解

    Mysql中LEFT JOIN和JOIN查詢區(qū)別及原理詳解

    這篇文章主要介紹了Mysql中LEFT JOIN和JOIN查詢區(qū)別及原理詳解,Nested Loop Join 實(shí)際上就是通過驅(qū)動(dòng)表的結(jié)果集作為循環(huán)基礎(chǔ)數(shù)據(jù),然后一條一條的通過該結(jié)果集中的數(shù)據(jù)作為過濾條件到下一個(gè)表中查詢數(shù)據(jù),然后合并結(jié)果,需要的朋友可以參考下
    2023-08-08
  • MySQL與Mongo簡單的查詢實(shí)例代碼

    MySQL與Mongo簡單的查詢實(shí)例代碼

    本文通過一個(gè)實(shí)例給大家用MySQL和mongodb分別寫一個(gè)查詢,本文圖片并茂給大家介紹的非常詳細(xì),感興趣的朋友參考下吧
    2016-10-10
  • MySQL定時(shí)任務(wù)EVENT事件的使用方法

    MySQL定時(shí)任務(wù)EVENT事件的使用方法

    本文主要介紹了MySQL定時(shí)任務(wù)EVENT事件的使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • MySQL中Next-Key Lock底層原理實(shí)現(xiàn)

    MySQL中Next-Key Lock底層原理實(shí)現(xiàn)

    Next-KeyLock是MySQL InnoDB存儲(chǔ)引擎中的一種鎖機(jī)制,結(jié)合記錄鎖和間隙鎖,用于高效并發(fā)控制并避免幻讀,本文主要介紹了MySQL中Next-Key Lock底層原理實(shí)現(xiàn),感興趣的可以了解一下
    2025-03-03
  • MySQL中符號(hào)@的作用

    MySQL中符號(hào)@的作用

    本文主要介紹了MySQL中符號(hào)@的作用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • 淺談MySQL InnoDB實(shí)現(xiàn)MVCC原理

    淺談MySQL InnoDB實(shí)現(xiàn)MVCC原理

    本文主要介紹了MySQL InnoDB實(shí)現(xiàn)MVCC原理,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2026-03-03
  • php 不能連接數(shù)據(jù)庫 php error Can''t connect to local MySQL server

    php 不能連接數(shù)據(jù)庫 php error Can''t connect to local MySQL server

    php 不能連接數(shù)據(jù)庫 php error Can't connect to local MySQL server through socket '/tmp/mysql.sock'
    2011-05-05
  • MySQL授權(quán)命令grant的使用方法小結(jié)

    MySQL授權(quán)命令grant的使用方法小結(jié)

    這篇文章主要介紹了MySQL授權(quán)命令grant的使用方法,本文實(shí)例,運(yùn)行于?MySQL?5.0?及以上版本,介紹了MySQL?賦予用戶權(quán)限命令的簡單格式,本文給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2021-12-12
  • MYSQL根據(jù)分組獲取組內(nèi)多條數(shù)據(jù)中符合條件的一條(實(shí)例詳解)

    MYSQL根據(jù)分組獲取組內(nèi)多條數(shù)據(jù)中符合條件的一條(實(shí)例詳解)

    這篇文章主要介紹了MYSQL根據(jù)分組獲取組內(nèi)多條數(shù)據(jù)中符合條件的一條,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • MySQL 元數(shù)據(jù)的使用小結(jié)

    MySQL 元數(shù)據(jù)的使用小結(jié)

    本文主要介紹了MySQL 元數(shù)據(jù)的使用,通過INFORMATION_SCHEMA數(shù)據(jù)庫訪問,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-08-08

最新評(píng)論

宁德市| 黎川县| 东丽区| 漳平市| 卓尼县| 溆浦县| 井陉县| 广昌县| 乌鲁木齐市| 赤水市| 双江| 乌兰县| 红河县| 云安县| 仙游县| 疏勒县| 新营市| 静宁县| 宣化县| 隆化县| 遂平县| 崇阳县| 长岭县| 辽宁省| 峡江县| 葵青区| 康平县| 天门市| 那曲县| 遂平县| 怀安县| 乳山市| 遵化市| 沙田区| 通州市| 延长县| 天门市| 南澳县| 铜川市| 灵寿县| 棋牌|