私は、子を削除できる一連の関係を扱っていますが、孫と親の間の接続を失いたくないのは明らかです。私は子供を「故人」としてマークすることを考えました(この投稿に関連する用語を使用するため)が、結局私は私のDBに死んだ子供たちの束で立ち往生し、それを望んでいます(関係を維持する目的でのみ) )?
親が削除されると、その子孫もすべて削除されます。さらに、それは「通常の」関係のように機能し、孫と子は常に同じトップレベルの親を持ちます。階層は3つのレベルに固定されています(上記を参照)。最後に、Parent、Child、Grandchildはすべて異なるタイプです(たとえば、3人の「人間」について話しているのではなく、同じベースを持っていません)。
ただし、その関係は通常、親子関係から派生できるため、孫に親を追跡させるのは少し奇妙に感じられます。しかし、私はそれを行う別の方法を考えることはできません。
このモデルは有効ですか?それとも別の方法がありますか?
実際に選択した構成とその理由を共有したかっただけです(現在のソリューションとは異なるため)。
Grandchild
を追跡するParent
に外部キーを追加するだけです。
PostgreSQLコードの例:
CREATE TABLE Parent (
id INT PRIMARY KEY
);
CREATE TABLE Child (
id INT PRIMARY KEY,
parent_id INT REFERENCES Parent(id) NOT NULL ON DELETE CASCADE
);
CREATE TABLE Grandchild (
id INT PRIMARY KEY,
child_id INT REFERENCES Child(id),
parent_id INT REFERENCES Parent(id) NOT NULL ON DELETE CASCADE
);
最後に、トリガーを使用して孫がParent
と同じChild
を持っていることを確認できます。
これはおそらくDB設計を非正規化していることを知っていますが、ここでの他の回答よりも実装がはるかに簡単で、ORMをそのまま使用しても問題なく機能するようです。親の祖父母が決して変わらない場合、これは(非正規化に関して)完全に細かい妥協案です。
私はこの解決策についての提案や意見を喜んで聞きます。
この回答は、各レベルが異なるタイプであるという説明の前に立っていたあなたの質問に基づいています。あなたはさまざまなタイプの必要性を識別したので、私はそれが 最初に現れた として私の答えに同意し、あなたの 自己回答があなたがどのようにアプローチしたかを文書化しますこの問題。 最上位のテーブルを参照する孫テーブルに単一の列を追加することが、最も簡単なアプローチのようです。
将来の訪問者に役立つように、以下の詳細はそのままにしておきます。
これを相互参照テーブルで実装します。
以下は、SQL Server固有の例です。このテーブルには、名前など、エンティティに関する列が含まれています。
CREATE TABLE dbo.Entities
(
EntityID int NOT NULL
CONSTRAINT PK_Entities
PRIMARY KEY CLUSTERED
IDENTITY(1,1)
, EntityName varchar(30) NOT NULL
);
次の表は、それらの関係を示しています。
CREATE TABLE dbo.EntityRelationships
(
EntityIDParent int NOT NULL
CONSTRAINT FK_EntityRelationships_Parent
FOREIGN KEY
REFERENCES dbo.Entities (EntityID)
, EntityIDChild int NOT NULL
CONSTRAINT FK_EntityRelationships_Child
FOREIGN KEY
REFERENCES dbo.Entities (EntityID)
, CONSTRAINT PK_EntityRelationships
PRIMARY KEY CLUSTERED (EntityIDParent, EntityIDChild)
, CONSTRAINT CK_EntitytRelationships
CHECK ((EntityIDParent <> EntityIDChild))
);
各関係は一意である必要があります。つまり、指定された親は、指定された子に一度だけ関連付けることができます。
次に、Entities
テーブルにINSTEAD OF DELETE
トリガーを作成します。これにより、削除されたエンティティを削除する前に、必要な関係を親に変更して、削除を適切に処理します。
CREATE TRIGGER EntityRelationshipDelete
ON dbo.Entities
INSTEAD OF DELETE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO dbo.EntityRelationships (EntityIDParent, EntityIDChild)
SELECT erp.EntityIDParent
, erc.EntityIDChild
FROM deleted d
INNER JOIN dbo.EntityRelationships erp ON d.EntityID = erp.EntityIDChild
INNER JOIN dbo.EntityRelationships erc ON d.EntityID = erc.EntityIDParent
EXCEPT --don't create duplicate entries
SELECT er.EntityIDParent, er.EntityIDChild
FROM dbo.EntityRelationships er;
DELETE
FROM dbo.EntityRelationships
FROM dbo.EntityRelationships er
INNER JOIN deleted d ON er.EntityIDChild = d.EntityID OR er.EntityIDParent = d.EntityID;
DELETE
FROM dbo.Entities
FROM dbo.Entities e
INNER JOIN deleted d ON e.EntityID = d.EntityID;
END;
GO
ここでは、そのセットアップをテストします。
INSERT INTO dbo.Entities (EntityName)
VALUES ('Grandparent')
, ('Parent')
, ('Child');
INSERT INTO dbo.EntityRelationships (EntityIDParent, EntityIDChild)
VALUES (1, 2)
, (2, 3);
SELECT Parents.EntityName
, Children.EntityName
FROM dbo.EntityRelationships er
INNER JOIN dbo.Entities Parents ON er.EntityIDParent = Parents.EntityID
INNER JOIN dbo.Entities Children ON er.EntityIDChild = Children.EntityID;
上記の選択の結果:
╔═════════════╦════════════╗ ║EntityName║EntityName║ ╠══════ ═══════╬════════════╣ ║祖父母║親║ ║親║子║ ╚═══ ══════════╩════════════╝
ここでは、「親」エンティティを削除し、関係を再クエリします。
DELETE
FROM dbo.Entities
WHERE dbo.Entities.EntityName = 'Parent';
SELECT Parents.EntityName
, Children.EntityName
FROM dbo.EntityRelationships er
INNER JOIN dbo.Entities Parents ON er.EntityIDParent = Parents.EntityID
INNER JOIN dbo.Entities Children ON er.EntityIDChild = Children.EntityID;
結果:
╔═════════════╦════════════╗ ║EntityName║EntityName║ ╠══════ ═══════╬════════════╣ ║祖父母║子供║ ╚═════════════ ╩════════════╝
DELETE FROM dbo.Entities
(WHERE
句なしで)を実行すると、bothテーブルからすべての行が削除されることに注意してください。
もう少し複雑な例を示すには、祖父母が2人、親が2人、子供が1人いるとします。
INSERT INTO dbo.Entities (EntityName)
VALUES ('Grandparent 1')
, ('Grandparent 2')
, ('Parent 1')
, ('Parent 2')
, ('Child');
INSERT INTO dbo.EntityRelationships (EntityIDParent, EntityIDChild)
VALUES (1, 3)
, (2, 3)
, (1, 4)
, (3, 5)
, (4, 5);
SELECT Parents.EntityName
, Children.EntityName
FROM dbo.EntityRelationships er
INNER JOIN dbo.Entities Parents ON er.EntityIDParent = Parents.EntityID
INNER JOIN dbo.Entities Children ON er.EntityIDChild = Children.EntityID;
╔═══════════════╦════════════╗ ║EntityName║EntityName║ ╠════ ═══════════╬════════════╣ ║祖父母1║親1║ ║祖父母1║親2║ ║祖父母2║親1║ ║親1║子║ ║親2║子║ ╚═══════════ ════╩════════════╝
Entities
テーブルからParent 1
を削除すると、次のようになります。
DELETE
FROM dbo.Entities
WHERE dbo.Entities.EntityName = 'Parent 1';
私たちはこれを見ます:
╔═══════════════╦════════════╗ ║EntityName║EntityName║ ╠════ ═══════════╬════════════╣ ║祖父母1║親2║ ║祖父母1║子║ ║祖父母2║子供║ ║親2║子供║ ╚═══════════════╩════════ ════╝
これにより、テストデータのクリーンアップが実行されます。
IF OBJECT_ID(N'dbo.EntityRelationships', N'U') IS NOT NULL
DROP TABLE dbo.EntityRelationships;
IF OBJECT_ID(N'dbo.Entities', N'U') IS NOT NULL
DROP TABLE dbo.Entities;
GO
ltree
PostgreSQLを使用している場合は、 ltree
をチェックアウトして、これを実行し、正常で索引付け可能な状態に保つことができます。
CREATE EXTENSION ltree; -- required if you don't have it.
CREATE TABLE test (path ltree);
INSERT INTO test VALUES ('Top');
INSERT INTO test VALUES ('Top.Science');
INSERT INTO test VALUES ('Top.Science.Astronomy');
INSERT INTO test VALUES ('Top.Science.Astronomy.Astrophysics');
これでTop.Science.Astronomy
を削除でき、Top.Science
の子孫であるすべての関係を@>
で簡単にクエリできます
DELETE FROM test
WHERE path = 'Top.Science.Astronomy';
SELECT *
FROM test
WHERE 'Top.Science' @> path;