【问题标题】:Hierarchical SQL Queries: Best SQL query to obtain the whole branch of a tree from a [nodeid, parentid] pairs table given the end node id分层 SQL 查询:在给定结束节点 id 的情况下,从 [nodeid, parentid] 对表中获取树的整个分支的最佳 SQL 查询
【发布时间】:2015-03-07 13:13:57
【问题描述】:

有没有办法在 SQL 中发送递归查询?

给定结束节点 ID,我需要按级别排序到根节点(具有 parentid = NULL)的所有行。例如。如果我有类似的东西:

nodeid | parentid
a      | NULL    
b      | a       
c      | b       

在查询end_node_id = c 后,我会得到类似的信息:

nodeid | parentid | depth
a      | NULL     | 0
b      | a        | 1
c      | b        | 2

(除了深度,我还可以使用到给定端节点的距离)

我能想到的唯一(也是显而易见的)方法是每行执行一次查询,直到到达父节点。

有没有更有效的方法?

【问题讨论】:

  • 你使用的是什么关系型数据库?
  • 它必须适用于 Oracle 和 Postgres(尽管通用解决方案是理想的)
  • WITH RECURSIVE (postgresql.org/docs/8.4/static/queries-with.html) 似乎适用于 Postgres。现在我需要一个 Oracle 解决方案
  • 在下面查看我的答案以获取 Oracle 解决方案

标签: sql tree hierarchical-data recursive-query hierarchical-query


【解决方案1】:

如果您使用的是 mssql 2005+,您可以这样做:

测试数据:

DECLARE @tbl TABLE(nodeId VARCHAR(10),parentid VARCHAR(10))

INSERT INTO @tbl
VALUES ('a',null),('b','a'),('c','b')

查询

;WITH CTE
AS
(
    SELECT
        tbl.nodeId,
        tbl.parentid,
        0 AS Depth
    FROM
        @tbl as tbl
    WHERE
        tbl.parentid IS NULL
    UNION ALL
    SELECT
        tbl.nodeId,
        tbl.parentid,
        CTE.Depth+1 AS Depth
    FROM
        @tbl AS tbl
        JOIN CTE
            ON tbl.parentid=CTE.nodeId
)
SELECT
    *
FROM
    CTE

【讨论】:

  • 这与接受的解决方案有相同的问题:我想提供一个端节点 ID 来获取分支而不是根节点 ID,但以这种方式获取它是一个简单的更改,所以我考虑这也是一个有效的答案。
【解决方案2】:

最终得到以下解决方案(其中 level 是到结束节点的距离)

Oracle,使用hierarchical queries(感谢@Mureinik 提供的信息):

SELECT     IDCATEGORY, IDPARENTCATEGORY, LEVEL
FROM       TNODES
START WITH IDCATEGORY=122
CONNECT BY IDCATEGORY = PRIOR IDPARENTCATEGORY;

使用视图的示例,因此它可以归结为单个标准 SQL 查询(需要 >= 10g):

CREATE OR REPLACE VIEW VNODES AS 
SELECT CONNECT_BY_ROOT IDCATEGORY "IDBRANCH", IDCATEGORY, IDPARENTCATEGORY, LEVEL AS LVL
FROM TNODES 
CONNECT BY IDCATEGORY = PRIOR IDPARENTCATEGORY;

SELECT * FROM VNODES WHERE IDBRANCH = 122 ORDER BY LVL ASC;

http://sqlfiddle.com/#!4/18ba80/3

Postgres >= 8.4,使用WITH RECURSIVE Common Table Expression 查询:

WITH RECURSIVE BRANCH(IDPARENTCATEGORY, IDCATEGORY, LEVEL) AS (
    SELECT IDPARENTCATEGORY, IDCATEGORY, 1 AS LEVEL FROM TNODES WHERE IDCATEGORY = 122
  UNION ALL
    SELECT p.IDPARENTCATEGORY, p.IDCATEGORY, LEVEL+1
    FROM BRANCH pr, TNODES p
    WHERE p.IDCATEGORY = pr.IDPARENTCATEGORY
  )
SELECT IDCATEGORY,IDPARENTCATEGORY, LEVEL
FROM BRANCH
ORDER BY LEVEL ASC

使用视图的示例,因此可以归结为单个标准 SQL 查询:

CREATE OR REPLACE VIEW VNODES AS 
WITH RECURSIVE BRANCH(IDBRANCH,IDPARENTCATEGORY,IDCATEGORY,LVL) AS (
  SELECT IDCATEGORY AS IDBRANCH, IDPARENTCATEGORY, IDCATEGORY, 1 AS LVL FROM TNODES
  UNION ALL
    SELECT pr.IDBRANCH, p.IDPARENTCATEGORY, p.IDCATEGORY, LVL+1
    FROM BRANCH pr, TNODES p
    WHERE p.IDCATEGORY = pr.IDPARENTCATEGORY
  )
SELECT IDBRANCH, IDCATEGORY, IDPARENTCATEGORY, LVL
FROM BRANCH;

SELECT * FROM VNODES WHERE IDBRANCH = 122 ORDER BY LVL ASC;

http://sqlfiddle.com/#!11/42870/2

【讨论】:

    【解决方案3】:

    对于 Oracle,根据 cmets 中的要求,您可以使用 connect by 运算符生成层次结构,并使用 level 伪列获取深度:

    SELECT     nodeid, parentid, LEVEL
    FROM       t
    START WITH parentid IS NULL
    CONNECT BY parentid = PRIOR nodeid;
    

    【讨论】:

    • 我已将其标记为已接受,因为 START WITH / CONNECT BY 正是我需要的 Oracle 解决方案,尽管实际查询略有不同(最终节点 ID 是提供的节点 ID,不是根节点ID)。检查我的答案以获得实际解决方案。
    • (对不起,我不接受您的回答,您向我展示了在 Oracle 中获得所需解决方案所需的确切内容,但我觉得我必须提供更详细的答案,以说明如何在 oracle 中使用视图和 postgresql 将问题简化为单个简单的标准 SQL 选择查询)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-19
    • 1970-01-01
    • 1970-01-01
    • 2019-08-15
    • 2010-11-24
    相关资源
    最近更新 更多