在另一个查询中使用表别名来遍历树

时间:2014-07-08 02:50:08

标签: mysql sql oracle sqlite postgresql

我有以下查询:

select * from (
   select p1.c as child, p1.p as parent, 1 as height
   from parent as p1
   where (p1.c=3 or p1.c=8);

   union

   select p2.c as child, p2.c as parent, 2 as height
   from parent as p2 
   where (p1.child=3 or p1.child=8) and p1.parent = p2.child;
)

架构是:

CREATE TABLE parent(p int, c int);

我试图找到从孩子 root 的路径。 [编辑]并追加我们必须遍历的边数 目标是将孩子的父母与其父母一起加入,例如:

(8, 2, 1)
(8, 5, 2) -> 8 is the lowest child, 2 is its parent, and 5 it's 2 parent.

一些示例数据:

10 | 5
10 | 12
12 | 3
12 | 4
4  | 6
4  | 7
5  | 11
5  | 2
2  | 8

如何在第二个查询p1中使用第一个查询p2的引用? 之后我应该;

(8,2,1)
(3,12,1)
(3,10,2)
(8,5,2)

因此,我已经知道如何完成我想要的工作。

3 个答案:

答案 0 :(得分:4)

问题

无法从同一级别(或UNION查询的另一个分支)中的另一个查询中的一个子查询引用表别名。表别名仅在查询本身及其子查询中可见 您可以使用LATERAL JOIN在同一查询级别引用子查询的输出列。例如:
Find most common elements in array with a group by

小最大级别的解决方案

仅限少数级别(如果知道最大值),您可以使用简单的查询:

  • LEFT JOIN到表格本身的n-1个实例
  • 使用COALESCECASE语句确定root和hight,

SELECT p1.c AS child, COALESCE(p3.p, p2.p, p1.p) AS parent
      ,CASE
          WHEN p3.p IS NOT NULL THEN 3
          WHEN p2.p IS NOT NULL THEN 2
          ELSE 1
       END AS height
FROM   parent p1
LEFT   JOIN parent p2 ON p2.c = p1.p
LEFT   JOIN parent p3 ON p3.c = p2.p
WHERE  p1.c IN (3, 8)
ORDER  BY p1.c;

这是标准SQL,应该在您标记的所有4个RDBMS 中

任意级别的通用解决方案

使用recursive CTE之类的@Ken已经建议。

  • 在递归的分支中为每一行保留孩子,只推进父母。
  • 在外部SELECT中,只保留每个孩子最大height的行。

WITH RECURSIVE cte AS (
   SELECT c AS child, p AS parent, 1 AS height
   FROM   parent
   WHERE  c IN (3, 8)

   UNION ALL

   SELECT c.child, p.p AS parent, c.height + 1
   FROM   cte    c
   JOIN   parent p ON p.c = c.parent
   -- WHERE  c.height < 10  -- to safeguard against endless loops if necessary
   )
SELECT DISTINCT ON (child) *
FROM   cte
ORDER  BY child, height DESC;

DISTINCT ON特定于 Postgres 。说明:
Select first row in each GROUP BY group?

其余的将在 Oracle 甚至 SQLite 中以类似的方式工作,但在不支持CTE的MySQL中则不行。

SQL Fiddle展示了两者。

答案 1 :(得分:2)

Postgres 中,我建议您查看WITH RECURSIVE CTE

http://www.postgresql.org/docs/9.3/static/queries-with.html

它非常适合建模树状结构,如父子关系,无论是整棵树,子树等等。

Postgres doc中有很多例子,包括专门处理树状结构,应该适用于你的情况(尽管你的查询有一些调整以适应{{1} }模具)。

答案 2 :(得分:2)

无法在第二个SELECT中引用第一个SELECT的表别名(在UNION或UNION ALL集合运算符之后。您可以在相关子查询中,但必须在相关子查询的上下文中)相同的SELECT。)

另外,你需要在语句中抛弃分号; MySQL将把这看作是声明的结尾。

对于 MySQL ,你肯定有一个可行的方法来解决这个问题,为每个&#34; height&#34;使用单独的查询,并将结果与​​{{ 1}}设置运算符。可能,您希望使用UNION集合运算符来提高性能。 (如果您不需要额外的步骤来识别和删除重复项。此外,它不需要使用内联视图。

UNION ALL

只是为了演示如何将其扩展到3的高度,将另一个JOIN添加到同一个表中,别名为SELECT p1.c AS child , p1.p AS parent , 1 AS height FROM parent p1 WHERE p1.c IN (3,8) UNION ALL SELECT p1.c AS child , p2.p AS parent , 2 AS height FROM parent p1 JOIN parent p2 ON p2.c = p1.p WHERE p1.c IN (3,8)

p3

注意

最初,我只注意到它被标记为MySQL。

使用Oracle,您可以使用递归 UNION ALL SELECT p1.c AS child , p3.p AS parent , 3 AS height FROM parent p1 JOIN parent p2 ON p2.c = p1.p JOIN parent p3 ON p3.c = p2.p WHERE p1.c IN (3,8) 形式的SELECT,以获得没有CONNECT BY的等效结果。