web-dev-qa-db-ja.com

n番目の子IDで最上位の親を取得しますか?

ここで、親子関係を維持するためにこの手法を一般的に使用するという質問があります。つまり、すべてのエンティティをparent_id列とすべてのトップを持つ1つのテーブルに格納しますほとんどの親は、parent_id列にを持っています。これは、正規化された優れた手法であると私は同意しますが、欠点もあり、遅くて非効率的です。これは主に、各親がツリーを作成するためにクエリを何度も実行する必要があるような再帰が原因です。

SELECT id FROM `table` WHERE parent_id=something

私は、クエリを何度も実行してサーバーに負荷をかけることでプログラミング言語でそれを実行しようとする可能性のあるソリューションを見てきました。ストアドプロシージャを提供しているものもありますが、これには再帰も含まれます。

だから私の質問は、深さを知っているか、それが可能かどうかわからないので、ツリー(結合またはサブクエリ)の1つのデータベースクエリでそれを行うことができるので、どのようにして最上位の親(つまり、parent_id = 0)を取得できるかです。 )それが不可能である場合、子供がいるのに、なぜこの手法が欠陥があるのに有名なのか、それとも別の解決策があるのですか? 。私はSQLフィドルを追加しましたが、スキーマしかありません

[〜#〜]フィドル[〜#〜]

2
M Khalid Junaid

あなたが探しているものは、この答えを書いている時点でMySQLによって促進されていないだけです。共通テーブル式(CTE)は、いくつかのデータベースでサポートされています。 @a_horse_with_no_name 回答を投稿 それらのRDBMSに名前を付けます。

私は再帰的なストアドプロシージャを投稿しました: 階層フィールドの最高レベルを検索:CTEありvsなし ただし、ストアドプロシージャを使用しないことを強調しました。あなたが求めていることを行うには、MySQLのストアドプロシージャが必要です。

妥協ソリューション...

ストアドプロシージャは引き続き必要ですが、再帰は使用しません。単純なループを使用して、自己結合クエリを作成し、動的に実行できます。あなたが探している木の深さを考えると、ここにそのようなストアドプロシージャがあります:

DELIMITER $$

DROP PROCEDURE IF EXISTS `dianuj`.`GetTopParentGivenDepth` $$
CREATE PROCEDURE `dianuj`.`GetTopParentGivenDepth` (GivenDepth INT)
BEGIN

    DECLARE x1,x2 INT;

    SET x = 0;
    SET y = 1;
    SET @SQ = 'SELECT DISTINCT A0.id FROM prarent A0';
    WHILE y < GivenDepth DO
        SET @SQ = CONCAT(@SQ,' INNER JOIN prarent A',y,' ON A',x,'.id = A',y,'.parent_id');
        SET x = y;
        SET y = x + 1;
    END WHILE;
    SET @SQ = CONCAT(@SQ,' WHERE A0.parent_id = 0');
    SELECT @SQ;
    PREPARE stmt FROM @SQ;
    EXECUTE stmt;
    DEALLOCATE PREPARE stmt;

END $$

DELIMITER ;

私のデスクトップ上のMySQLにデータをロードした後、私はストアドプロシージャを実行しました...

深さ1

mysql> call GetTopParentGivenDepth(1);
+--------------------------------------------------------------+
| @SQLSTMT                                                     |
+--------------------------------------------------------------+
| SELECT DISTINCT A0.id FROM prarent A0 WHERE A0.parent_id = 0 |
+--------------------------------------------------------------+
1 row in set (0.00 sec)

+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
+----+
3 rows in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql>

深さ2

mysql> call GetTopParentGivenDepth(2);
+------------------------------------------------------------------------------------------------------------+
| @SQLSTMT                                                                                                   |
+------------------------------------------------------------------------------------------------------------+
| SELECT DISTINCT A0.id FROM prarent A0 INNER JOIN prarent A1 ON A0.id = A1.parent_id WHERE A0.parent_id = 0 |
+------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

+----+
| id |
+----+
|  3 |
|  2 |
+----+
2 rows in set (0.01 sec)

Query OK, 0 rows affected (0.01 sec)

mysql>

深さ3 .. 6

mysql> call GetTopParentGivenDepth(3);
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
| @SQLSTMT                                                                                                                                                 |
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
| SELECT DISTINCT A0.id FROM prarent A0 INNER JOIN prarent A1 ON A0.id = A1.parent_id INNER JOIN prarent A2 ON A1.id = A2.parent_id WHERE A0.parent_id = 0 |
+----------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

+----+
| id |
+----+
|  3 |
+----+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql> call GetTopParentGivenDepth(4);
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| @SQLSTMT                                                                                                                                                                                               |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| SELECT DISTINCT A0.id FROM prarent A0 INNER JOIN prarent A1 ON A0.id = A1.parent_id INNER JOIN prarent A2 ON A1.id = A2.parent_id INNER JOIN prarent A3 ON A2.id = A3.parent_id WHERE A0.parent_id = 0 |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

+----+
| id |
+----+
|  3 |
+----+
1 row in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql> call GetTopParentGivenDepth(5);
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| @SQLSTMT                                                                                                                                                                                                                                             |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| SELECT DISTINCT A0.id FROM prarent A0 INNER JOIN prarent A1 ON A0.id = A1.parent_id INNER JOIN prarent A2 ON A1.id = A2.parent_id INNER JOIN prarent A3 ON A2.id = A3.parent_id INNER JOIN prarent A4 ON A3.id = A4.parent_id WHERE A0.parent_id = 0 |
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

+----+
| id |
+----+
|  3 |
+----+
1 row in set (0.01 sec)

Query OK, 0 rows affected (0.01 sec)

mysql> call GetTopParentGivenDepth(6);
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| @SQLSTMT                                                                                                                                                                                                                                                                                           |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| SELECT DISTINCT A0.id FROM prarent A0 INNER JOIN prarent A1 ON A0.id = A1.parent_id INNER JOIN prarent A2 ON A1.id = A2.parent_id INNER JOIN prarent A3 ON A2.id = A3.parent_id INNER JOIN prarent A4 ON A3.id = A4.parent_id INNER JOIN prarent A5 ON A4.id = A5.parent_id WHERE A0.parent_id = 0 |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

Empty set (0.00 sec)

Query OK, 0 rows affected (0.01 sec)

mysql>

Depth 6には戻り値がありません。最大深度を見つけるには、行が戻らなくなるまでそれを反復する必要があります。

警告

  • 最悪のシナリオは、テーブルの行数を深さとして使用することです。このようなツリーは、退化したリンクリストにすぎません。
  • SQLの構築やその後の実行など、クエリのパフォーマンスについては保証できません。あらゆる種類の再帰は、クエリパーサーの責任です。

試してみる !!!

1
RolandoMySQLDBA