由于这些“from”和“to”关系是双向的(您需要遍历两个方向),因此在 MySQL 中没有简单的语句可以做到这一点。要在单个列中返回单个结果集中的所有节点值,我可以避免 UNION 操作的最接近方法是:
SELECT CASE
WHEN t.i = 1 THEN t.dnode
WHEN t.i = 2 AND t.dnode = c.fromnode THEN c.tonode
WHEN t.i = 2 AND t.dnode = c.tonode THEN c.fromnode
ELSE NULL
END AS node
FROM ( SELECT d.i
, m.root
, CASE WHEN m.root = n.fromnode THEN n.tonode ELSE n.fromnode END AS dnode
FROM (SELECT 'node1' AS root) m
CROSS
JOIN (SELECT 1 AS i UNION ALL SELECT 2) d
LEFT
JOIN conn n ON m.root IN (n.fromnode,n.tonode)
) t
LEFT
JOIN conn c
ON t.i = 2 AND t.dnode IN (c.fromnode,c.tonode)
GROUP BY node
ORDER BY node
我不知道我是否能够解开它,但我会尝试的。为了避免多次指定根节点“node1”,我使用子查询来返回它。
(SELECT 'node1' AS root) m
因为我们要“深入两层”,所以我需要两组节点,所以我创建了一个笛卡尔积来使我拥有的行数加倍,我将它们标记为 1 用于第一级,2为第二级。
CROSS
JOIN (SELECT 1 AS i UNION ALL SELECT 2) d
有了这个,我现在准备加入conn
表,并且我想要任何具有与根节点匹配的 fromnode 或 tonode 值的行。
LEFT
JOIN conn n ON m.root IN (n.fromnode,n.tonode)
使用该结果集,我想在其中一些行上“翻转”fromnode 和 tonode,这样我们基本上总是在一侧有“根”节点。我使用 CASE 表达式来测试哪一侧与根匹配:
CASE WHEN m.root = n.fromnode THEN n.tonode ELSE n.fromnode END AS dnode
所以现在我将该结果集包装为内联视图 aliased t
。该子查询可以单独运行,以查看我们是否返回了我们期望的内容:
SELECT d.i
, m.root
, CASE WHEN m.root = n.fromnode THEN n.tonode ELSE n.fromnode END AS dnode
FROM (SELECT 'node1' AS root) m
CROSS
JOIN (SELECT 1 AS i UNION ALL SELECT 2) d
LEFT
JOIN conn n ON m.root IN (n.fromnode,n.tonode)
我们确实需要返回那个“级别”值(d.i
我们之前生成的,我们在下一步需要它,当我们再次加入 conn 表时,遍历下一个级别,我只需要加入我们要去的那些行看第二层。
LEFT
JOIN conn c
ON t.i = 2 AND t.dnode IN (c.fromnode,c.tonode)
再说一次,我不在乎哪个侧节点在这一点上,我只需要进行匹配即可。
此时,您可以运行整个查询,然后拉动t.*, c.*
看看我们有什么,但我将跳过这部分并直接进入“魔术”。
此时,我们可以使用 CASE 表达式从混乱中“挑选”出我们想要的节点值。
如果级别值(不幸地标记为i
)是 1,那么我们正在查看第一个级别,因此我们只需要获取位于“根”的“另一”侧的“nodeX”值。这可以从t
源代码中获得,作为别名为dnode
.
否则,我们将查看“第二”级的行,i = 2
. (在这种特殊情况下,i = 2
可以省略对 on 的测试,但为了完整性,它包括在内,以防万一我们将扩展这种方法以获得三个(喘气!)或更多(哦,我的!)级别。
在这里,我们需要知道哪一侧(从或到)与第一级匹配,我们只需拉另一侧。如果t.dnode
一侧匹配,我们拉另一侧。
最后,我们使用 GROUP BY 来折叠重复项。
由于我们不关心这些来自哪个级别,因此我们省略了返回t.i
,这将为我们提供级别。
概括
我不认为这比您的查询更简单。而且我不知道性能如何比较。但是很高兴有其他陈述来比较性能。