1

我有两列的表(varchar from,varchar to)。该表表示节点之间的连接(从节点到节点)。我想让所有节点从我指定的节点连接或连接到我指定的节点,以及从这些节点连接或连接到这些节点的节点。目前我使用下面的查询给我正确的结果,但我正在寻找更整洁的解决方案。

//currently used query specified node "node1"
SELECT tonode as node
FROM conn
WHERE
fromnode
IN
(SELECT tonode as node FROM conn WHERE fromnode="node1"
UNION
SELECT fromnode as node FROM conn WHERE tonode="node1")
UNION
SELECT fromnode as node
FROM conn
WHERE
tonode
IN
(SELECT tonode as node FROM conn WHERE fromnode="node1"
UNION
SELECT fromnode as node FROM conn WHERE tonode="node1")


//create table for conn table
CREATE TABLE `conn` (
  `fromnode` varchar(70) NOT NULL,
  `tonode` varchar(70) NOT NULL,
  PRIMARY KEY (`fromnode`,`tonode`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
INSERT INTO `conn` (`fromnode`,`tonode`) VALUES
 ('node1','node2'),
 ('node1','node3'),
 ('node3','node2'),
 ('node4','node1'),
 ('node4','node2'),
 ('node4','node5'),
 ('node5','node6'),
 ('node4','node3');
4

3 回答 3

3

我的优化版本:

SET @origin = "node1";
SELECT DISTINCT
 IF(c1.fromnode = @origin,
   IF(c1.tonode = c2.tonode,
     IF(c2.fromnode = @origin, c2.tonode, c2.fromnode),
     IF(c2.tonode = @origin, c2.fromnode, c2.tonode)
   ),
   IF(c1.fromnode = c2.tonode,
     IF(c2.fromnode = @origin, c2.tonode, c2.fromnode),
     IF(c2.tonode = @origin, c2.fromnode, c2.tonode)
   )
 ) AS node
FROM conn AS c1
LEFT JOIN conn AS c2 ON (c1.fromnode = c2.fromnode OR c1.tonode = c2.fromnode OR c1.fromnode = c2.tonode OR c1.tonode = c2.tonode)
WHERE c1.fromnode = @origin OR c1.tonode = @origin;

旧查询的 DESCRIBE 输出:

+----+--------------------+------------+--------+---------------+---------+---------+------------+------+--------------------------+
| id | select_type        | table      | type   | possible_keys | key     | key_len | ref        | rows | Extra                    |
+----+--------------------+------------+--------+---------------+---------+---------+------------+------+--------------------------+
|  1 | PRIMARY            | conn       | index  | NULL          | PRIMARY | 424     | NULL       |    8 | Using where; Using index |
|  2 | DEPENDENT SUBQUERY | conn       | eq_ref | PRIMARY       | PRIMARY | 424     | const,func |    1 | Using where; Using index |
|  3 | DEPENDENT UNION    | conn       | eq_ref | PRIMARY       | PRIMARY | 424     | func,const |    1 | Using where; Using index |
| NULL | UNION RESULT       | <union2,3> | ALL    | NULL          | NULL    | NULL    | NULL       | NULL |                          |
|  4 | UNION              | conn       | index  | NULL          | PRIMARY | 424     | NULL       |    8 | Using where; Using index |
|  5 | DEPENDENT SUBQUERY | conn       | eq_ref | PRIMARY       | PRIMARY | 424     | const,func |    1 | Using where; Using index |
|  6 | DEPENDENT UNION    | conn       | eq_ref | PRIMARY       | PRIMARY | 424     | func,const |    1 | Using where; Using index |
| NULL | UNION RESULT       | <union5,6> | ALL    | NULL          | NULL    | NULL    | NULL       | NULL |                          |
| NULL | UNION RESULT       | <union1,4> | ALL    | NULL          | NULL    | NULL    | NULL       | NULL |                          |
+----+--------------------+------------+--------+---------------+---------+---------+------------+------+--------------------------+

我的查询的 DESCRIBE 输出:

+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------------------------------------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra                                     |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------------------------------------+
|  1 | SIMPLE      | c1    | index | PRIMARY       | PRIMARY | 424     | NULL |    8 | Using where; Using index; Using temporary |
|  1 | SIMPLE      | c2    | index | PRIMARY       | PRIMARY | 424     | NULL |    8 | Using index                               |
+----+-------------+-------+-------+---------------+---------+---------+------+------+-------------------------------------------+
于 2012-06-27T21:10:10.747 回答
0

如果我对您的理解正确(大约只有 2 个级别),您可以执行以下操作:

SELECT level,fromnode , tonode 
FROM conn1 
WHERE level < 3
CONNECT BY PRIOR tonode = fromnode
START WITH fromnode like '%';
于 2012-06-27T21:27:52.363 回答
0

由于这些“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,这将为我们提供级别。


概括

我不认为这比您的查询更简单。而且我不知道性能如何比较。但是很高兴有其他陈述来比较性能。

于 2012-06-27T23:01:55.827 回答