2010-03-03 18 views
5

Mam tabeli SQL zdefiniowane jak poniżej:kompozytowe klucze podstawowe i Klucz obcy błąd ograniczenie

CREATE TABLE [TestComposite] ( 
    ID int, 
    SiteUrl nvarchar(255), 
    Name nvarchar(max) NOT NULL, 
    ParentID int NULL, 
    PRIMARY KEY (ID, SiteUrl) 
); 

przedmioty i foldery są przechowywane wewnątrz tej samej tabeli, jeśli element jest wewnątrz folderu, kolumna ParentID jest ID folderu. Chciałbym móc usuwać elementy/foldery CASCADE po usunięciu folderu.

Przykładem może być bardziej wyraźne:

INSERT INTO [TestComposite] VALUES (1, 'site1', 'Item1', NULL) 
INSERT INTO [TestComposite] VALUES (2, 'site1', 'Item2', NULL) 
INSERT INTO [TestComposite] VALUES (3, 'site1', 'Folder1', NULL) 
INSERT INTO [TestComposite] VALUES (4, 'site1', 'Folder1.Item1', 3) 
INSERT INTO [TestComposite] VALUES (5, 'site1', 'Folder1.Item2', 3) 
INSERT INTO [TestComposite] VALUES (6, 'site1', 'Folder1.Folder1', 3) 
INSERT INTO [TestComposite] VALUES (7, 'site1', 'Folder1.Folder1.Item1', 6) 
etc... 

Więc jeśli usunę element 3 (folder), chcę przedmioty/foldery 4, 5, 6 i 7, które mają zostać usunięte zbyt.

Próbowałem dodać ograniczenie podobny do:

ALTER TABLE [TestComposite] 
ADD CONSTRAINT fk_parentid 
FOREIGN KEY (ParentID, SiteUrl) 
REFERENCES [TestComposite] (ID, SiteUrl) ON DELETE CASCADE; 

Ale to daje mi ten błąd:
Wprowadzenie ograniczenia klucz obcy „fk_parentid” na stole „TestComposite” może powodować cykli lub wiele ścieżek kaskadowe. Określ ON DELETE NO ACTION lub ON UPDATE NO ACTION lub zmodyfikuj inne ograniczenia klucza OBCEGO.

Próbowałem również dodać drugą kolumnę SiteUrl o nazwie ParentSiteUrl, na wypadek gdyby problem polegał na tym, że kolumna nie może być częścią tego samego FK/PK, ale mam ten sam komunikat o błędzie.

Czy robię coś nie tak?

Dziękuję

Odpowiedz

5

Załóż ON DELETE NO ACTION ograniczenia i wykorzystywać to, aby usunąć wszystkie rekordy i ich dzieci:

WITH q AS 
     (
     SELECT id, SiteURL 
     FROM TestComposite 
     WHERE id = 3 
       AND SiteURL = 'site1' 
     UNION ALL 
     SELECT tc.id, tc.SiteURL 
     FROM q 
     JOIN TestComposite tc 
     ON  tc.ParentID = q.Id 
       AND tc.SiteURL = q.SiteURL 
     ) 
DELETE 
FROM TestComposite 
WHERE EXISTS 
     (
     SELECT id, SiteURL 
     INTERSECT 
     SELECT * 
     FROM q 
     ) 
+0

Mogę się mylić, ponieważ jeszcze tego nie przetestowałem, ale nie sądzę, żeby w moim przypadku usunięto element 7, ponieważ znajduje się on na innym poziomie hierarchii?! –

+0

'@ OmaR': tak, usunie także' 7'. To rekurencyjny "CTE". – Quassnoi

+0

Dziękuję, działa świetnie. Nie wiedziałem o rekurencyjnym CTE. Wygląda na to, że działa zarówno na SQL Server 2005, jak i na SQL Server 2008. –

0

myślę, co chcesz zrobić, można osiągnąć dodając nową kolumnę o nazwie parentId, a następnie zadeklarować ją jako klucz obcy z kluczem podstawowym. W ten sposób problem zostanie rozwiązany i nadal możesz zrobić wszystko, co tylko chcesz.

+0

Niestety, nie rozumiem, co chcesz mi zrobić ?! –

0

Problem polega na tym, że tworzysz możliwość kaskad rekurencyjnych - gdy każdy usunięty przez kaskadę może utworzyć dowolną liczbę kolejnych usunięć. MS SQL nie obsługuje tego. Spróbuj usunąć je ręcznie w swoim kodzie. BTW nie polecam usuwania kaskadowego.

http://support.microsoft.com/kb/321843

+0

Jak powiedziałem Ardmanowi, jeśli nie mam złożonego klucza podstawowego, powiedzmy tylko ID, byłbym w stanie stworzyć rekursywny klucz obcy bez problemu. STWÓRZ TABELĘ myTable (ID int PRIMARY KEY, Nazwa nvarchar (max), ParentID int); ALTER TABLE myTable ADD CONSTRAINT fk_parentID FOREIGN KEY (ParentID) REFERENCJE myTable (ID) ON CASCADE DELETE; Nie?! –

+0

nie, nie możesz! Właśnie próbowałem. nie chodzi o klucz złożony, czy nie, chodzi o to, aby utworzyć rekursję na kaskadzie, a serwer SQL nie chce tego zrobić samemu. – Andrey

+0

To dziwne, przysiągłbym, że już to zrobiłem ... przetestuję to ponownie :) –

1

Jeśli masz SQL Server 2008, korzystania z HierarchyID typu do tej pracy.

+0

Dziękuję, sprobuję to –

+0

powitanie OmaR. –