1

2 つの列 (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

「2 レベルの深さ」に進むため、2 セットのノードが必要です。そのため、取得した行数を 2 倍にするデカルト積を作成し、最初のレベルに 1、2 にラベルを付けます。 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

そのため、その結果セットをエイリアス化されたインライン ビューとしてラップします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 テーブルに再び結合するときに必要です。次のレベルをトラバースします。目的の行を結合するだけで済みます。 2 番目のレベルを見てください。

  LEFT
  JOIN conn c
    ON t.i = 2 AND t.dnode IN (c.fromnode,c.tonode)

繰り返しになりますが、どちらの側のノードがオンになっているかは気にしません。この時点では、マッチを実行するだけです。

この時点で、クエリ全体を実行し、t.*, c.*取得したものをプルして確認することもできますが、その部分はスキップして、「魔法」に直行します。

この時点で、CASE 式を使用して、その混乱から必要なノード値を「選択」できます。

レベル値 (残念ながら とラベル付けされiています) が 1 の場合、最初のレベルを見ているので、「ルート」の「反対側」にあった「nodeX」値を取得する必要があります。tこれは、としてエイリアス化された式としてソースから入手できますdnode

それ以外の場合は、「第 2」レベルの行を見ていきますi = 2。(この特定のケースでは、上のテストはi = 2省略できますが、完全を期すために、このアプローチを拡張して 3 つ (あえぎ!) またはそれ以上 (おっと!) のレベルを取得する場合に備えて、hear が含まれています。

ここでは、どちらの側 (from または to) が最初のレベルに一致したかを知る必要があり、反対側をプルするだけです。片側が一致する場合t.dnodeは、反対側を引っ張ります。

最後に、GROUP BY を使用して重複を折りたたみます。

これらがどのレベルからのものであるかは気にしないので、レベルを返す を省略t.iします。


まとめ

これはあなたのクエリよりも簡単だとは思いません。そして、パフォーマンスがどのように比較されるかはわかりません。ただし、パフォーマンスを比較する他のステートメントがあると便利です。

于 2012-06-27T23:01:55.827 に答える