4

idparent_id、およびname列を持つ組織テーブルがあります。このテーブルには約 50,000 行あります。最上位の親は 1 つだけで、残りはすべてその下にあります。levelOracle では、疑似列を使用して特定の組織の現在の深さを簡単に取得できます。

SELECT id, parent_id, level, name
FROM organizations
START WITH parent_id = 1
CONNECT BY PRIOR id = parent_id

MySQLで上記を行う適切な方法が何であるかがわかりません。1 つのクエリでノードの深さと一緒にツリー全体を取得する必要があります。

これに関係する StackOverflow に関する多くの質問がありますが、どれも本当に良い答えを持っているようには見えず、ほとんどが疑わしい解決策を持つブログへのリンクです。確かに、これはある種の簡単な方法で実行可能ですか?

残念ながら、テーブルを何らかの方法で変更することはオプションではないため、ネストされたセットは可能ではありません。

4

4 に答える 4

4

これは完全に陽気です。昨日、文字通り同様の質問で+50の報奨金を受け取りました: Using MySQL query to traverse rows to make a recursive tree

DBA StackExchange のストアド プロシージャでこれを行う方法を参照しました(2011 年 10 月 24 日)

同じストアド プロシージャを、DBA StackExchange の回答の例とともに投稿します。

特定のノードの親を取得するコード

DELIMITER $$
DROP FUNCTION IF EXISTS `junk`.`GetParentIDByID` $$
CREATE FUNCTION `junk`.`GetParentIDByID` (GivenID INT) RETURNS INT
DETERMINISTIC
BEGIN
    DECLARE rv INT;

    SELECT IFNULL(parent_id,-1) INTO rv FROM
    (SELECT parent_id FROM pctable WHERE id = GivenID) A;
    RETURN rv;
END $$
DELIMITER ;

特定のノードの祖先を取得するコード

DELIMITER $$
DROP FUNCTION IF EXISTS `junk`.`GetAncestry` $$
CREATE FUNCTION `junk`.`GetAncestry` (GivenID INT) RETURNS VARCHAR(1024)
DETERMINISTIC
BEGIN
    DECLARE rv VARCHAR(1024);
    DECLARE cm CHAR(1);
    DECLARE ch INT;

    SET rv = '';
    SET cm = '';
    SET ch = GivenID;
    WHILE ch > 0 DO
        SELECT IFNULL(parent_id,-1) INTO ch FROM
        (SELECT parent_id FROM pctable WHERE id = ch) A;
        IF ch > 0 THEN
            SET rv = CONCAT(rv,cm,ch);
            SET cm = ',';
        END IF;
    END WHILE;
    RETURN rv;
END $$
DELIMITER ;

任意のノードのファミリー ツリー (または子孫) を取得するコード

DELIMITER $$

DROP FUNCTION IF EXISTS `junk`.`GetFamilyTree` $$
CREATE FUNCTION `junk`.`GetFamilyTree` (GivenID INT) RETURNS varchar(1024) CHARSET latin1
DETERMINISTIC
BEGIN

    DECLARE rv,q,queue,queue_children VARCHAR(1024);
    DECLARE queue_length,front_id,pos INT;

    SET rv = '';
    SET queue = GivenID;
    SET queue_length = 1;

    WHILE queue_length > 0 DO
        SET front_id = FORMAT(queue,0);
        IF queue_length = 1 THEN
            SET queue = '';
        ELSE
            SET pos = LOCATE(',',queue) + 1;
            SET q = SUBSTR(queue,pos);
            SET queue = q;
        END IF;
        SET queue_length = queue_length - 1;

        SELECT IFNULL(qc,'') INTO queue_children
        FROM (SELECT GROUP_CONCAT(id) qc
        FROM pctable WHERE parent_id = front_id) A;

        IF LENGTH(queue_children) = 0 THEN
            IF LENGTH(queue) = 0 THEN
                SET queue_length = 0;
            END IF;
        ELSE
            IF LENGTH(rv) = 0 THEN
                SET rv = queue_children;
            ELSE
                SET rv = CONCAT(rv,',',queue_children);
            END IF;
            IF LENGTH(queue) = 0 THEN
                SET queue = queue_children;
            ELSE
                SET queue = CONCAT(queue,',',queue_children);
            END IF;
            SET queue_length = LENGTH(queue) - LENGTH(REPLACE(queue,',','')) + 1;
        END IF;
    END WHILE;

    RETURN rv;

END $$

すべての実行を実証するために、サンプル データを次に示します。

USE junk
DROP TABLE IF EXISTS pctable;
CREATE TABLE pctable
(
    id INT NOT NULL AUTO_INCREMENT,
    parent_id INT,
    PRIMARY KEY (id)
) ENGINE=MyISAM;
INSERT INTO pctable (parent_id) VALUES (0);
INSERT INTO pctable (parent_id) SELECT parent_id+1 FROM pctable;
INSERT INTO pctable (parent_id) SELECT parent_id+2 FROM pctable;
INSERT INTO pctable (parent_id) SELECT parent_id+3 FROM pctable;
INSERT INTO pctable (parent_id) SELECT parent_id+4 FROM pctable;
INSERT INTO pctable (parent_id) SELECT parent_id+5 FROM pctable;
SELECT * FROM pctable;

すべての親、祖先、および家系図を表示するクエリは次のとおりです。

SELECT
    id,parent_id,
    GetParentIDByID(id),
    GetAncestry(id),
    GetFamilyTree(id)
FROM pctable;

試してみる !!!

于 2012-06-15T17:43:08.080 に答える
2

CTE/サブクエリ ファクタリングを使用しない SQL には再帰はありません。そのため、特に MySQL に実装されている「純粋な SQL」で、任意のレベルの深さでこれを行う方法は特にありません。MSSQL CTE クエリを MySQL に変換する方法を参照してください。

そのため、あなたが見つけた答えはすべてハックであり、エンジンのこの非常に特定の制限を回避するためのものです. それでも必要な場合は、このソリューションを試すことができます: Generating Depth based tree from Hierarchical Data in MySQL (no CTEs)。(もちろん、この制限は、LangSec の意味でのクエリの解析が奇妙ではないことを意味します。これにより、あらゆる種類の有益な解析が可能になり、セキュリティ上の懸念が解消されますが、まったく役に立ちません。)

階層を使い果たす一連のLefts Joins が残っているだけです。失望させて悪いしゃれを使用して申し訳ありません。

于 2012-06-15T15:58:57.340 に答える
0

@idok、複数の子に同じ親IDがある場合、関数は「切り捨てられた不正なDOUBLE値」エラーを返します。dbaから次の修正を見つけました。これはSivakumar Natarayanによって提供されました

WHILE queue_length > 0 DO
    IF queue_length = 1 THEN
    SET front_id = queue;
        SET queue = '';
    ELSE
    SET front_id = SUBSTR(queue,1,LOCATE(',',queue)-1);
        SET pos = LOCATE(',',queue) + 1;
        SET q = SUBSTR(queue,pos);
        SET queue = q;
    END IF;
于 2020-10-15T08:37:41.680 に答える
-1

Mysql は再帰的 sql をサポートしていません。ディメンションが 1 つしかない場合は、parent_id = id でそれ自体との Left 結合を使用できます。

于 2012-06-15T17:18:45.033 に答える