在關係數據庫(SQL)中,我有一個可以有0..n個相關子實體的父實體。父母實體部分由其相關子實體的集合來唯一標識,這樣我就不應該有兩個類似的父母擁有同一個孩子集合。實施相關實體的唯一性
所以我可以有父1子1和子項2,和父母2子2和兒童3,但我不能有兒童2兒童另一名家長3.
理想情況下,我想使用數據庫約束強制執行此唯一性。我曾考慮過將所有子記錄的散列存儲到父項中,但是想知道是否有更簡單/更標準的方法來完成此操作。
任何想法?
在關係數據庫(SQL)中,我有一個可以有0..n個相關子實體的父實體。父母實體部分由其相關子實體的集合來唯一標識,這樣我就不應該有兩個類似的父母擁有同一個孩子集合。實施相關實體的唯一性
所以我可以有父1子1和子項2,和父母2子2和兒童3,但我不能有兒童2兒童另一名家長3.
理想情況下,我想使用數據庫約束強制執行此唯一性。我曾考慮過將所有子記錄的散列存儲到父項中,但是想知道是否有更簡單/更標準的方法來完成此操作。
任何想法?
這種約束是棘手,因爲SQL沒有關係平等的運營商,即evaluting A = B,其中A的沒有簡單的方法和B是成組的行。標準SQL確實支持嵌套表,但不幸的是SQL Server不支持。
一個可能的答案是類似下面的斷言,這將檢查表中的任一相同的家庭:
NOT EXISTS (
SELECT 1
FROM family f, family g
WHERE f.child = g.child
AND f.parent <> g.parent
GROUP BY f.parent, g.parent
HAVING COUNT(*) = (SELECT COUNT(*) FROM family WHERE parent = f.parent)
AND COUNT(*) = (SELECT COUNT(*) FROM family WHERE parent = g.parent)
)
注意,此查詢不試圖處理不帶孩子的家庭。在集合論上,兩個空集合必然是相同的。如果你想允許無子女的家庭,那麼你將不得不決定是否應該認定兩個無子女的家庭是否相同。
SQL不是一種真正的關係語言,它遠遠不夠關係語言應該具備的功能。教程D是的支持關係相等和關係值屬性的真正關係語言的一個例子。在教程D中,原則上可以將每個家族表示爲relvar中單個屬性的值。這個家庭屬性也可能是一個關鍵,因此不允許重複的家庭。
不錯,看起來像我們有類似的想法......這是光滑的,似乎它會比我的回答表現更好。謝謝。 –
我太倉促了。我原來的查詢沒有給出正確的結果,所以我編輯了它。 – sqlvogel
感謝那些建議使用觸發器的人的幫助。這大致是我所擁有的,似乎正在發揮作用。
CREATE TRIGGER [dbo].[trig_Parent_Child_Uniqueness]
ON [dbo].[Parent_Child]
AFTER INSERT, UPDATE
AS
BEGIN
IF EXISTS (
SELECT 1
FROM Parent p1
--Compare each pair of parents
JOIN Parent p2 ON p1.ParentId <> p2.ParentId
WHERE NOT EXISTS (
--Find any children that are different
SELECT 1
FROM (
SELECT ChildId FROM Parent_Child c1
WHERE c1.ParentId = p1.ParentId
) as c1
FULL OUTER JOIN (
SELECT ChildId FROM Parent_Child c2
WHERE c2.ParentId = p2.ParentId
) as c2 ON c2.ChildId = c1.ChildId
WHERE c1.ChildId IS NULL OR c2.ChildId IS NULL
)
) ROLLBACK;
END;
編輯:或者更好的解決方案,從@sqlvogel改編
CREATE TRIGGER [dbo].[trig_Parent_Child_Uniqueness]
ON [dbo].[Parent_Child]
AFTER INSERT, UPDATE
AS
BEGIN
IF EXISTS (
SELECT 1
FROM Parent_Child p1
FULL JOIN Parent_Child p2 ON p1.ParentId <> p2.ParentId
AND p1.ChildId = p2.ChildId
GROUP BY p1.ParentId
HAVING COUNT(p1.ParentId) = COUNT(*)
AND COUNT(p2.ParentId) = COUNT(*)
) ROLLBACK;
END;
這是一個有點噁心的,因爲它包括觸發器和光標:(
它包括在父表中的列,其是基於孩子
設置:
CREATE TAble Parent
(
Id INT Primary Key,
Name VARCHAR(50),
ChildItems VARCHAR(200) NOT NULL UNIQUE
)
CREATE TABLE Child
(
Id INT Primary Key,
Name VARCHAR(50)
)
CREATE TABLE ParentChild
(
Id INT Identity Primary Key,
ParentId INT,
ChildId Int
)
觸發器
-- This gives the unique colmn a default based upon the id of the parent
CREATE TRIGGER trg_Parent ON Parent
INSTEAD OF Insert
AS
SET NOCOUNT ON
INSERT INTO Parent (Id, Name, ChildItems)
SELECT Id, Name, '/' + CAST(Id As Varchar(10)) + '/'
FROM Inserted
GO
-- This updates the parent with a path based upon child items
-- If a the exact same child items exist for another parent then this fails
-- because of the unique index
CREATE Trigger trg_ParentChild ON ParentChild
AFTER Insert, Update
AS
DECLARE @ParentId INT = 0
DECLARE @ChildItems VARCHAR(8000) = ''
DECLARE parentCursor CURSOR FOR
SELECT DISTINCT ParentId
FROM Inserted
OPEN parentCursor
FETCH NEXT FROM parentCursor INTO @ParentId
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT @ChildItems = COALESCE(@ChildItems + '/ ', '') + CAST(ChildID As Varchar(10))
FROM ParentChild
WHERE ParentId = @ParentId
ORDER BY ChildId
UPDATE Parent
SET ChildItems = @ChildITems
WHERE Id = @ParentId
FETCH NEXT FROM parentCursor INTO @ParentId
SET @ChildItems = ''
END
CLOSE parentCursor
DEALLOCATE parentCursor
GO
數據設置
INSERT INTO Parent (Id, Name)
VALUES (1, 'Parent1'), (2,'Parent2'), (3, 'Parent3')
INSERT INTO Child (Id, Name)
VALUES (1,'Child1'), (2,'Child2'), (3,'Child3'), (4,'Child4')
現在插入一些數據
-- This one succeeds
INSERT INTO ParentChild (ParentId, ChildId)
VALUES (1,1),(1,2),(2,2),(2,3)
-- This one Fails
INSERT INTO ParentChild (ParentId, ChildId) VALUES (3,1),(3,2)
棘手。我們是否允許帶有子1,2 *和* 3的「父母3」來擴展您的示例?另外,請記住哈希值告訴你兩件事情是完全不同的,但*不要*告訴你兩件事情是一樣的。 –
另外,還有一個(合理的)兒童總數的上限? –
@Damien_The_Unbeliever是的。我們可以讓一個父親擁有另一個集合的超集,但是沒有兩個父母的集合應該是平等的。至於哈希,我希望能夠使它足夠大,使碰撞機會可以忽略不計。我會說我需要哈希來區分不超過100個獨特的兒童組合。 –