分层 SQL 查询:最佳 SQL 查询从 [nodeid, parentid] 对 table 中获取树的整个分支给定结束节点 id
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,我需要按级别排序直至根节点(具有 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
(除了深度我还可以使用到给定端节点的距离)
我能想到的唯一(也是显而易见的)方法是每行执行一个查询,直到到达父节点。
有没有更有效的方法?
如果您使用的是 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
对于 Oracle,按照评论中的要求,您可以使用 connect by
运算符来生成层次结构,并使用 level
伪列来获取深度:
SELECT nodeid, parentid, LEVEL
FROM t
START WITH parentid IS NULL
CONNECT BY parentid = PRIOR nodeid;
最终得到以下解决方案(其中 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;
有什么方法可以在 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
(除了深度我还可以使用到给定端节点的距离)
我能想到的唯一(也是显而易见的)方法是每行执行一个查询,直到到达父节点。
有没有更有效的方法?
如果您使用的是 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
对于 Oracle,按照评论中的要求,您可以使用 connect by
运算符来生成层次结构,并使用 level
伪列来获取深度:
SELECT nodeid, parentid, LEVEL
FROM t
START WITH parentid IS NULL
CONNECT BY parentid = PRIOR nodeid;
最终得到以下解决方案(其中 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;