2009-10-28 13 views
15

w MSSQL 2005 Właśnie uderzył niesławny komunikat o błędzie:Na czym polega problem z kluczem obcym kaskadowania wielu ścieżek i cykli?

wprowadzania obcego ograniczenie klucza XXX YYY na stole 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.

Teraz StackOverflow ma kilka tematów dotyczących tego komunikatu o błędzie, więc mam już rozwiązanie (w moim przypadku będę musiał użyć wyzwalaczy), ale jestem ciekaw, dlaczego jest taki problem w ogóle.

Jak rozumiem, istnieją zasadniczo dwa scenariusze, których chcą uniknąć - cykl i wiele ścieżek. Cykl będzie polegał na tym, że dwie tablice kaskadują sobie klucze obce. OK, cykl może obejmować również kilka tabel, ale jest to podstawowy przypadek i łatwiej go przeanalizować.

Wiele ścieżek byłoby wtedy, gdy TableA ma obce klucze do TableB i TableC, a TableB ma również klucz obcy do TableC. Znowu - jest to minimalny podstawowy przypadek.

Nie widzę żadnych problemów, które pojawiłyby się, gdy rekord zostałby usunięty lub zaktualizowany w dowolnej z tych tabel. Oczywiście, może być konieczne wielokrotne wysyłanie zapytań do tej samej tabeli, aby sprawdzić, które rekordy wymagają aktualizacji/usunięcia, ale czy to naprawdę problem? Czy to jest problem z wydajnością?

W innych tematach dotyczących SO ludzie sięgają aż do etykietowania za pomocą kaskad jako "risky" i określają "resolving cascade paths is a complex problem". Czemu? Gdzie jest ryzyko? Gdzie jest problem?

Odpowiedz

4

Masz tabelę podrzędną z 2 ścieżkami kaskadowymi od tego samego rodzica: jeden "usuń", jeden "null".

Co ma pierwszeństwo? Czego oczekujesz później? itp.

Uwaga: wyzwalacz jest kodem i może dodać do kaskady pewną inteligencję lub warunki.

0

Zgadzam się z tym, że kaskady są "ryzykowne" i należy ich unikać. (Osobiście wolę kaskadowanie zmian ręcznie, a raczej posiadanie serwera sql automatycznie się nimi zajmie). Jest to również dlatego, że nawet jeśli serwer sql usunięte miliony wierszy, wyjście będzie nadal widoczny jako

(1 row (s) affected)

+1

Czy zastąpienie go wyzwalaczem czyniłoby to lepszym? Oczywiście - istnieje takie podejście, jak "nie używanie obcych kluczy w ogóle" i robienie wszystkiego z kodu. Nie sądzę, że muszę ci powiedzieć, dlaczego myślę, że to zły pomysł ... –

1

Powodem zabraniamy korzystania kaskadę usuwać ma do czynienia z wydajnością i blokowaniem . Tak, nie jest tak źle, gdy usuniesz jeden rekord, ale wcześniej czy później będziesz musiał usunąć dużą grupę rekordów, a twoja baza danych przestanie działać.

W przypadku usuwania wystarczającej liczby rekordów program SQL Server może eskalować do blokady tabeli i nikt nie może nic zrobić z tabelą, dopóki jej nie zakończy.

Niedawno przenieśliśmy jednego z naszych klientów na jego własny serwer. W ramach umowy musieliśmy także usunąć wszystkie rekordy tego klienta z naszego oryginalnego serwera. Usunięcie wszystkich swoich informacji w partiach (aby nie powodować problemów z innymi użytkownikami) zajęło kilka miesięcy. Gdybyśmy mieli skonfigurowane usuwanie kaskadowe, baza danych byłaby niedostępna dla innych klientów przez długi czas, ponieważ miliony rekordów zostały usunięte w jednej transakcji, a setki tabel były zablokowane do czasu zakończenia transakcji.

Mogłem również zobaczyć scenariusz, w którym mógł wystąpić zakleszczenie przy użyciu kaskadowego kasowania, ponieważ nie mamy kontroli nad kolejnością, jaką podjąłaby ścieżka kaskadowa, a nasza baza danych jest nieco denormalizowana, ponieważ w większości tabel występuje identyfikator klienta. Więc jeśli zablokował jeden stół, który miał klucz obcy również do trzeciej tabeli, a także tabelę klientów, która była w różnej ścieżce, prawdopodobnie nie mógł sprawdzić tej tabeli, aby usunąć z trzeciej tabeli, ponieważ to wszystko jedna transakcja i blokady nie zostaną zwolnione, dopóki nie zostanie zrobione. Być może nie pozwoliłoby to nam na kasowanie kaskadowe, gdyby zauważyło możliwość utworzenia zakleszczenia w transakcji.

Innym powodem uniknięcia kaskadowych usunięć jest to, że czasami istnienie rekordu podrzędnego jest wystarczającym powodem, aby nie usuwać rekordu nadrzędnego. Na przykład, jeśli masz tabelę klientów, a klient miał zamówienia w przeszłości, nie chcesz go usunąć i stracić informacji o faktycznym zamówieniu.

+4

Jeśli masz do czynienia z milionami wierszy, to prawie zawsze musisz używać trudnych metod, takich jak dozowanie, wyłączanie kluczy obcych, uruchamianie zadania w bezczynności itd. Ale to nie jest codzienny scenariusz. Ludzie przygotowują się na tego typu rzeczy z góry. Testują go w środowisku testowym. Robią długie i zakręcone skrypty, które podkręcają DB, aby uzyskać akceptowalną wydajność. A kiedy już to zrobi, zostanie zapomniane. Ale "ON DELETE CASCADE" ma być używany w życiu codziennym, kiedy nie usuwasz milionów wierszy, ale co najwyżej kilkaset na raz. –

+0

I zakleszczenia na kaskadach (lub nawet prostych usunięciach dla kilku wierszy w tym samym stole) mogą się zdarzyć w każdym razie - zapobieganie wielu ścieżkom i cyklom kaskadowym nie czyni jednego kawałka, aby uczynić go lepszym. –

1

Rozważmy tabelę pracowników:

CREATE TABLE Employee 
(
    EmpID INTEGER NOT NULL PRIMARY KEY, 
    Name VARCHAR(40) NOT NULL, 
    MgrID INTEGER NOT NULL REFERENCES Employee(EmpID) ON DELETE CASCADE 
); 

INSERT INTO Employees( 1, "Bill", 1); 
INSERT INTO Employees( 23, "Steve", 1); 
INSERT INTO Employees(234212, "Helen", 23); 

Załóżmy teraz Bill emeryturę:

DELETE FROM Employees WHERE Name = "Bill"; 

Ooooppps; wszyscy zostali zwolnieni!

[Możemy debatować, czy szczegóły składni są poprawne; koncepcja stoi, tak myślę.]

+13

Twierdzę, że w tym przypadku ON KASKADA KOREKTA była używana niepoprawnie. Przykro nam, to twoja wina, jeśli stworzysz błędny program - to nie jest praca języka programowania, aby uchronić cię przed pisaniem złego kodu. –

-1

Wydaje mi się, że opcja KASOWANIA NA KASOWANIU jest kwestią wprowadzanego modelu biznesowego. Relacja między dwoma obiektami biznesowymi może być albo prostym "powiązaniem", w którym oba końce relacji są powiązane, ale poza tym niezależnymi obiektami, których cykl życiowy jest inny i kontrolowany przez inną logikę. Istnieją jednak również relacje "agregacji", w których jeden obiekt może być postrzegany jako "rodzic" lub "właściciel" obiektu "podrzędnego" lub "szczegółowego". Istnieje jeszcze silniejsze pojęcie relacji "składu", w której przedmiot istnieje wyłącznie jako kompozycja wielu części. W przypadku "asocjacji" zwykle nie można zadeklarować ograniczenia NA KASK. Jednak w przypadku agregacji lub kompozycji funkcja ON DELETE CASCADE ułatwia dokładniejsze i bardziej deklaratywne odwzorowanie modelu biznesowego w bazie danych. To dlatego denerwuje mnie, że MS SQL Server ogranicza korzystanie z tej opcji do pojedynczej ścieżki kaskadowej. Jeśli się nie mylę, wiele innych powszechnie używanych systemów baz danych SQL nie narzuca takich ograniczeń.

+0

I to jest odpowiedź ... jak? –

1

Myślę, że problem polega na tym, że jeśli utworzysz jedną ścieżkę "NA KASOWANIU DELETE" i drugą "NA USUŃ OGRANICZENIA" lub "BEZ AKCJI" wynik (wynik) jest nieprzewidywalny. Zależy to od tego, który usuń-wyzwalacz (jest to również wyzwalacz, ale nie musisz samemu budować) zostanie wykonany jako pierwszy.

+0

Przy transakcjach nie powinno to stanowić problemu - wiersz zostaje usunięty, kasuje kaskadę, wtedy usuwanie zostaje ograniczone, a cała rzecz wycofuje się. – Brilliand

Powiązane problemy