Ograniczenia klucza obcego: kiedy należy używać NA AKTUALIZACJĘ i NA USUWANIU

195

Projektuję schemat bazy danych za pomocą MySQL Workbench, co jest całkiem fajne, ponieważ możesz robić diagramy i konwertuje je: P

W każdym razie zdecydowałem się użyć InnoDB ze względu na obsługę klucza obcego. Jedną z rzeczy, które zauważyłem, jest to, że umożliwia ustawienie opcji Aktualizuj i Usuń dla kluczy obcych. Czy ktoś może wyjaśnić, gdzie w prostym przykładzie można użyć „Ogranicz”, „Kaskada” i ustaw wartość null?

Powiedzmy na przykład, że mam usertabelę, która zawiera userID. I powiedzmy, że mam tabelę komunikatów, messagektóra jest wiele do wielu, która ma 2 klucze obce (które odnoszą się do tego samego klucza podstawowego userIDw usertabeli). Czy ustawienie opcji Aktualizuj i Usuwaj jest przydatne w tym przypadku? Jeśli tak, to który wybrać? Jeśli to nie jest dobry przykład, czy mógłbyś wymyślić dobry przykład, aby zilustrować, jak mogą być one przydatne?

Dzięki

meltuhamy
źródło

Odpowiedzi:

485

Nie wahaj się nałożyć ograniczeń na bazę danych. Na pewno będziesz mieć spójną bazę danych, a to jeden z dobrych powodów, aby korzystać z bazy danych. Zwłaszcza jeśli masz kilka aplikacji, które o to proszą (lub tylko jedną aplikację, ale z trybem bezpośrednim i trybem wsadowym używającym różnych źródeł).

Z MySQL nie masz zaawansowanych ograniczeń, tak jak w PostgreSQL, ale przynajmniej ograniczenia klucza obcego są dość zaawansowane.

Weźmy przykład tabeli firmowej ze tabelą użytkowników zawierającą osoby z tej firmy

CREATE TABLE COMPANY (
     company_id INT NOT NULL,
     company_name VARCHAR(50),
     PRIMARY KEY (company_id)
) ENGINE=INNODB;

CREATE TABLE USER (
     user_id INT, 
     user_name VARCHAR(50), 
     company_id INT,
     INDEX company_id_idx (company_id),
     FOREIGN KEY (company_id) REFERENCES COMPANY (company_id) ON...
) ENGINE=INNODB;

Spójrzmy na klauzulę ON UPDATE :

  • PRZY OGRANICZENIU AKTUALIZACJI : wartość domyślna : jeśli spróbujesz zaktualizować identyfikator_firmy w tabeli FIRMA, silnik odrzuci operację, jeśli przynajmniej jeden UŻYTKOWNIK przynajmniej wyświetli linki do tej firmy.
  • PRZY AKTUALIZACJI BRAK DZIAŁANIA : tak samo jak OGRANICZENIE.
  • PRZY AKTUALIZACJI KASKADU : zwykle najlepszy : jeśli zaktualizujesz identyfikator_firmy w wierszu tabeli FIRMA, silnik odpowiednio go zaktualizuje we wszystkich wierszach UŻYTKOWNIKA odnoszących się do tej FIRMY (ale nie aktywowano żadnych wyzwalaczy w tabeli USER, ostrzeżenie). Silnik będzie śledził zmiany dla Ciebie, jest dobry.
  • ON UPDATE SET NULL : jeśli zaktualizujesz identyfikator_firmy w wierszu tabeli FIRMA, silnik ustawi powiązanych UŻYTKOWNIKÓW identyfikator_firmy na NULL (powinien być dostępny w polu USER nazwa_firmy). W aktualizacji nie widzę nic interesującego, ale mogę się mylić.

A teraz po stronie ON DELETE :

  • PRZY USUNIĘCIU OGRANICZENIA : wartość domyślna : jeśli spróbujesz usunąć identyfikator id_firmy w tabeli FIRMA, silnik odrzuci operację, jeśli przynajmniej jeden UŻYTKOWNIK przynajmniej linki do tej firmy, może uratować ci życie.
  • PRZY USUNIĘCIU BRAK DZIAŁANIA : to samo co OGRANICZENIE
  • PRZY USUNIĘCIU KASKADY : niebezpieczne : jeśli usuniesz wiersz firmy w tabeli FIRMA, silnik usunie również powiązanych UŻYTKOWNIKÓW. Jest to niebezpieczne, ale można go użyć do automatycznego czyszczenia tabel pomocniczych (więc może to być coś, czego chcesz, ale na pewno nie dla przykładu FIRMA <-> UŻYTKOWNIKA)
  • USUŃ USUŃ NULL : garść : jeśli usuniesz wiersz FIRMY, powiązani UŻYTKOWNICY automatycznie będą mieli związek z NULL. Jeśli wartość Null to wartość dla użytkowników bez firmy, może to być dobre zachowanie, na przykład może trzeba zatrzymać użytkowników w aplikacji, jako autorów niektórych treści, ale usunięcie firmy nie stanowi dla ciebie problemu.

zazwyczaj moją domyślną wartością jest: PRZY USUNIĘCIU OGRANICZENIA PRZY AKTUALIZACJI KASKADY . z niektórymi ON DELETE CASCADEdla tabel śledzenia (dzienniki - nie wszystkie dzienniki - itp.), a ON DELETE SET NULLgdy tabela główna jest „prostym atrybutem” dla tabeli zawierającej klucz obcy, jak tabela JOB dla tabeli USER.

Edytować

Minęło dużo czasu, odkąd to napisałem. Teraz myślę, że powinienem dodać jedno ważne ostrzeżenie. MySQL ma jedno duże udokumentowane ograniczenie z kaskadami. Kaskady nie uruchamiają wyzwalaczy . Jeśli więc masz wystarczającą pewność siebie w tym silniku, aby używać wyzwalaczy, powinieneś unikać ograniczeń kaskadowych.

Wyzwalacze MySQL aktywują się tylko dla zmian wprowadzonych do tabel przez instrukcje SQL. Nie aktywują się w przypadku zmian w widokach ani zmian w tabelach dokonanych przez interfejsy API, które nie przesyłają instrukcji SQL do serwera MySQL

==> Zobacz poniżej ostatniej edycji, sprawy idą w tej domenie

Wyzwalacze nie są aktywowane przez działania na kluczu obcym.

I nie sądzę, że zostanie to kiedyś naprawione. Ograniczeniami klucza obcego zarządza pamięć InnoDb, a wyzwalacze są zarządzane przez silnik SQL MySQL. Oba są rozdzielone. Innodb to jedyna pamięć masowa z zarządzaniem ograniczeniami, może kiedyś dodadzą wyzwalacze bezpośrednio w silniku pamięci, a może nie.

Ale mam własne zdanie na temat tego, który element należy wybrać między słabą implementacją wyzwalacza a bardzo przydatną obsługą ograniczeń kluczy obcych. A kiedy już przyzwyczaisz się do spójności bazy danych, pokochasz PostgreSQL.

12/2017 - Aktualizacja tej edycji o MySQL:

jak stwierdził @IstiaqueAhmed w komentarzach, sytuacja uległa zmianie w tym temacie. Kliknij link i sprawdź rzeczywistą aktualną sytuację (która może ulec zmianie w przyszłości).

regilero
źródło
8
ON DELETE CASCADE : dangerous- weź ze szczyptą soli.
poniedziałek
3
Musisz uważać na kaskadowanie, może zablokować system, jeśli trzeba zmienić wiele rekordów. Kasowanie kasowania powinno być dokładnie sprawdzone przed użyciem, często naprawdę chcesz, aby usuwanie nie występowało, jeśli istnieją rekordy potomne. Nie chciałbym, aby klient usunął dane finansowe, które wcześniej usunął. Czasami lepiej jest upewnić się, że kakadowanie nie jest włączone, i zapewnić sposób na zlikwidowanie rekordów jako nieaktywnych.
HLGEM
1
Z punktu widzenia logiki biznesowej, jest jeden przypadek, które mogą być interesujące SET NULLw sposób ON UPDATE: uaktualnianie firmę reprezentuje oddział Spółki> Relacja Użytkownika. Np .: jeśli firma zmieni typ działalności, poprzedni użytkownicy mogą już nie być z nią powiązani, dlatego NULLmoże być preferowany dla tego indeksu.
CPHPython
1
@regilero, wygląda na to, że zawartość twojego pierwszego linku ( dev.mysql.com/doc/refman/5.6/en/triggers.html ) do strony mysql uległa zmianie. Mówi This includes changes to base tables that underlie updatable viewszamiast tego, co They do not activate for changes in views
wkleiłeś,
6
„Nie chciałbym, aby klient usunął dane finansowe z zamówień, które wcześniej zamówił”. W takiej sytuacji prawdopodobnie nadal potrzebujesz danych klienta. Twój projekt powinien prawdopodobnie oznaczać klienta jako nieaktywnego, nie usuwając jego wiersza z bazy danych. W praktyce moim doświadczeniem zawodowym jest to, że naprawdę bardzo rzadko chcesz coś usuwać , woląc domyślnie oznaczać nieaktywne. W przypadkach, w których trwałe usunięcie jest w porządku, CASCADE DELETEogólnie jest również w porządku, nawet preferowane. Nie uważam tego za szczególnie niebezpieczne.
GrandOpener
3

Dodatek do odpowiedzi @ MarkR - należy zauważyć, że wiele frameworków PHP z ORM nie rozpoznaje zaawansowanej konfiguracji DB (klucze obce, usuwanie kaskadowe, unikalne ograniczenia) ani nie korzysta z nich, a to może powodować nieoczekiwane zachowanie.

Na przykład, jeśli usuniesz rekord przy użyciu ORM, a DELETE CASCADEusuniesz rekordy w powiązanych tabelach, próba usunięcia tych powiązanych rekordów przez ORM (często automatycznie) spowoduje błąd.

lxa
źródło
11
Byłby to powód, aby nie używać tej konkretnej ORM. Każde narzędzie, które jest tak słabe w obsłudze bazy danych, nie jest godne zaufania. Klucze obce i kaskadowe usuwanie lub aktualizacje są podstawami db, a nie zaawansowanymi koncepcjami i nie należy nigdy projektować rzeczywistej bazy danych bez ograniczeń dla kluczy obcych!
HLGEM
Problem polega na tym, że zgłaszają błędy. Czy można OGRANICZYĆ USUWANIE, ale silnik nie generuje błędów, ale nadal utrzymuje semantykę? Chciałbym, aby mój program nadal działał, a jednocześnie chronił inne dane przed usunięciem.
TheRealChx101
2

Musisz to rozważyć w kontekście aplikacji. Ogólnie rzecz biorąc, powinieneś zaprojektować aplikację, a nie bazę danych (baza danych jest po prostu częścią aplikacji).

Zastanów się, jak Twoja aplikacja powinna reagować na różne przypadki.

Domyślnym działaniem jest ograniczenie (tzn. Niedozwolenie) operacji, co zwykle jest tym, czego chcesz, ponieważ zapobiega głupim błędom programowania. Jednak w USUŃ KASKADĘ może być również przydatne. To zależy od twojej aplikacji i tego, jak zamierzasz usunąć określone obiekty.

Osobiście użyłbym InnoDB, ponieważ nie powoduje to usunięcia twoich danych (por. MyISAM, co robi), a nie dlatego, że ma ograniczenia FK.

MarkR
źródło