0

次の形式の MySQL テーブルがあります。

CREATE  TABLE IF NOT EXISTS `Company` (
  `CompanyId` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
  `Name` VARCHAR(45) NULL ,
  `Address` VARCHAR(45) NULL ,
  `ParentCompanyId` INT UNSIGNED NULL ,
  PRIMARY KEY (`CompanyId`) ,
  INDEX `fk_Company_Company_idx` (`ParentCompanyId` ASC) ,
  CONSTRAINT `fk_Company_Company`
    FOREIGN KEY (`ParentCompanyId` )
    REFERENCES `Company` (`CompanyId` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

明確にするために、親会社を持つことができる会社があります。これにより、次のテーブル コンテンツの例が得られる可能性があります。

CompanyId    Name    Address        ParentCompanyId
1            Foo     Somestreet 3   NULL
2            Bar     Somelane 4     1
3            McD     Someway 1337   1
4            KFC     Somewhere 12   2
5            Pub     Someplace 2    4

さて、私の質問です。CompanyId 2 のすべての子を再帰的に取得したいと考えています。したがって、次の結果セットが表示されます。

CompanyId    Name    Address        ParentCompanyId
4            KFC     Somewhere 12   2
5            Pub     Someplace 2    4

ステートメントを使用することを考えWith ... AS ...ましたが、MySQL ではサポートされていません。私が考えた別の解決策は、結果セットを返すプロシージャまたは関数を使用し、それをその関数の再帰呼び出しと結合することでした。ただし、MySQL は戻り値として列タイプのみをサポートします。

最後に考えた解決策は、CompanyId と HasChildId の 2 つのフィールドを持つテーブルを作成することでした。次に、会社を再帰的にループし、会社 ID によってすべての再帰的な子でテーブルを埋めるプロシージャを作成できます。この場合、このテーブルを結合するクエリを書くことができます:

SELECT CompanyId, Name, Address
FROM   Company C -- The child
INNER JOIN CompanyChildMappingTable M
        ON M.CompanyId = C.HasChildId
INNER JOIN Company P -- The parent
        ON P.CompanyId = M.CompanyId
WHERE P.CompanyId = 2;

このオプションは、24 時間ごとにプロシージャを呼び出し、新しいレコードが に挿入されたときにその場でテーブルを埋める場合、高速なオプションである必要がありますCompany。しかし、これは非常に難しい可能性があるため、Company テーブルにトリガーを記述してこれを行う必要があります。

あなたのアドバイスを聞きたいです。

解決策: テーブルを埋めるために次のプロシージャを作成しました (現在は SELECT の結果を返すだけです)。

DELIMITER $$
DROP PROCEDURE IF EXISTS CompanyFillWithSubCompaniesByCompanyId$$

CREATE PROCEDURE CompanyFillWithSubCompaniesByCompanyId(IN V_CompanyId BIGINT UNSIGNED, IN V_TableName VARCHAR(100))
BEGIN

    DECLARE V_CONCAT_IDS VARCHAR(9999) DEFAULT '';
    DECLARE V_CURRENT_CONCAT VARCHAR(9999) DEFAULT '';

    SET V_CONCAT_IDS = (SELECT GROUP_CONCAT(CompanyId) FROM Company WHERE V_CompanyId IS NULL OR ParentCompanyId = V_CompanyId);
    SET V_CURRENT_CONCAT = V_CONCAT_IDS;

    IF V_CompanyId IS NOT NULL THEN

        companyLoop: LOOP

            IF V_CURRENT_CONCAT IS NULL THEN
                LEAVE companyLoop;
            END IF;

            SET V_CURRENT_CONCAT = (SELECT GROUP_CONCAT(CompanyId) FROM Company WHERE FIND_IN_SET(ParentCompanyId, V_CURRENT_CONCAT));
            SET V_CONCAT_IDS = CONCAT_WS(',', V_CONCAT_IDS, V_CURRENT_CONCAT);

        END LOOP;

    END IF;

    SELECT * FROM Company WHERE FIND_IN_SET(CompanyId, V_CONCAT_IDS);

END$$
4

2 に答える 2