次の形式の 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$$