【问题标题】:sqlite: how does order by in recursive queries work?sqlite:递归查询中的 order by 如何工作?
【发布时间】:2016-12-13 13:03:20
【问题描述】:

https://www.sqlite.org/lang_with.html#withorderby 展示了递归级别的排序如何将树的深度优先顺序更改为广度优先顺序的示例。理查德·希普 http://sqlite.1065341.n5.nabble.com/why-does-the-recursive-example-sort-alphabetically-td80404.html 说递归部分的order-by有一个特殊的含义:决定递归的顺序。

我已经在递归级别以外的字段上使用 order-by 并且......我无法预测结果。 :( 有人可以解释一下它是如何工作的吗?

基本上,让我们看一个类似于 sqlite 文档(上面的第一个链接)中的 Alice/Bob/Cindy 的示例,但名称有点混淆(在树中不是按字母顺序排列,而是以随机顺序插入)然后在递归部分使用“按名称排序”(而不是递归级别)。

    CREATE TABLE child_parent(
    name TEXT PRIMARY KEY,
    parent TEXT
    );
    INSERT INTO child_parent VALUES('KKK','HHH');
    INSERT INTO child_parent VALUES('HHH','LLL');
    INSERT INTO child_parent VALUES('MMM','CCC');
    INSERT INTO child_parent VALUES('CCC','QQQ');
    INSERT INTO child_parent VALUES('QQQ','LLL');
    INSERT INTO child_parent VALUES('TTT','QQQ');
    INSERT INTO child_parent VALUES('AAA','HHH');
    INSERT INTO child_parent VALUES('EEE','TTT');
    INSERT INTO child_parent VALUES('UUU','CCC');
    INSERT INTO child_parent VALUES('FFF','MMM');
    INSERT INTO child_parent VALUES('LLL',NULL);

child_parent 结构是:

    -- LLL -> QQQ
    --              -> TTT
    --                      -> EEE
    --              -> CCC
    --                      -> UUU
    --                      -> MMM
    --                              -> FFF
    --       -> HHH
    --              -> AAA
    --              -> KKK

首先选择 - 使用“ORDER BY new_name asc”

    WITH RECURSIVE
    tree(name,level) AS (
        SELECT cp.name, 0
            FROM child_parent cp
            WHERE cp.parent IS NULL
        UNION ALL
        SELECT ch.name AS new_name, tree.level+1 AS new_lvl
            FROM child_parent ch
                 INNER JOIN tree ON ch.parent=tree.name
        ORDER BY new_name ASC
    )
    SELECT *, substr('...................',1,level*3) || name FROM tree;
    -- name        level       substr('...................',1,level*3) || name
    -- ----------  ----------  -----------------------------------------------
    -- LLL         0           LLL                                            
    -- HHH         1           ...HHH                                         
    -- AAA         2           ......AAA                                      
    -- KKK         2           ......KKK                                      
    -- QQQ         1           ...QQQ                                         
    -- CCC         2           ......CCC                                      
    -- MMM         3           .........MMM                                   
    -- FFF         4           ............FFF                                
    -- TTT         2           ......TTT                                      
    -- EEE         3           .........EEE                                   
    -- UUU         3           .........UUU                                   

首先选择 - 使用“ORDER BY new_name desc”

    WITH RECURSIVE
    tree(name,level) AS (
        SELECT cp.name, 0
            FROM child_parent cp
            WHERE cp.parent IS NULL
        UNION ALL
        SELECT ch.name AS new_name, tree.level+1 AS new_lvl
            FROM child_parent ch
                 INNER JOIN tree ON ch.parent=tree.name
        ORDER BY new_name DESC
    )
    SELECT *, substr('...................',1,level*3) || name FROM tree;
    -- name        level       substr('...................',1,level*3) || name
    -- ----------  ----------  -----------------------------------------------
    -- LLL         0           LLL                                            
    -- QQQ         1           ...QQQ                                         
    -- TTT         2           ......TTT                                      
    -- HHH         1           ...HHH                                         
    -- KKK         2           ......KKK                                      
    -- EEE         3           .........EEE                                   
    -- CCC         2           ......CCC                                      
    -- UUU         3           .........UUU                                   
    -- MMM         3           .........MMM                                   
    -- FFF         4           ............FFF                                
    -- AAA         2           ......AAA                                      

基本上问题是:如何考虑数据库行为来预测最后两个查询的结果将如上所示。您能否描述一步一步发生的事情,例如在每个递归级别?

SQLite 3.15.2.0.37

【问题讨论】:

    标签: recursion sqlite recursive-query


    【解决方案1】:

    documentation 说:

    计算递归表内容的基本算法如下:

    1. 运行初始选择并将结果添加到队列中。
    2. 虽然队列不为空:
      一种。从队列中提取一行。
      湾。将该单行插入递归表
      C。假设刚刚提取的单行是递归表中的唯一行并运行递归选择,将所有结果添加到队列中。

    […]
    如果存在 ORDER BY 子句,它将确定在步骤 2a 中从队列中提取行的顺序。

    将 ORDER BY 应用于树级别列时,结果是广度优先或深度优先搜索。 (使用 ASC,首先提取“旧”行;使用 DESC,首先提取最低级别的最新行。)

    但当应用于其他列时,顺序不再与树的结构相关。

    【讨论】:

      【解决方案2】:

      回答

      哦,我明白了,所以队列只包含一个单行列表,我错过了两点:

      • 队列在每个小步骤之后重新排序,而不是在每个递归级别之后
      • 结果会在从队列中处理行时添加到结果中,而不是在添加到队列中时。

      分步示例

      示例:所以在上面的最后一种情况下(ORDER BY new_name DESC):

      • 初始选择 => 结果:空,队列 (LLL)
      • 第二部分递归选择基于队列的第一条记录
        • 结果:(LLL),队列为空
        • 我们得到 2 条记录:QQQ,HHH
        • 它们都按 ORDER BY 的顺序添加到队列中:队列 {QQQ, HHH}
      • 队列中的第一条记录被拾取
        • 结果:(LLL),(QQQ);排队 {HHH}
        • 首先基于 QQQ,我们获得了 2 条新记录:TTT 和 AAA
        • 它们被添加到队列中,但此时(!)队列将被排序,实际上是 {TTT, HHH, AAA}
      • 队列中的第一条记录被拾取
        • 结果:(LLL),(QQQ),(TTT);队列 {HHH, AAA}
        • 基于 TTT 的选择不会向队列中添加任何内容
      • 队列中的第一条记录被拾取:
        • 结果:(LLL),(QQQ),(TTT),(HHH);排队 {AAA}
        • 基于HHH的查询导致两个新行:KKK和CCC,这一步之后的队列是{KKK, CCC, AAA}
      • 等等

      总结

      • 是的,我们确实从上面的选择中得到了结果
      • 在按递归级别排序的情况下,这些步骤确实会导致深度优先或广度优先。

      谢谢!

      【讨论】:

        猜你喜欢
        • 2018-04-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-01-27
        相关资源
        最近更新 更多