Niektórzy bardzo dobrzy projektanci używają wartości NULL w kluczach obcych bez negatywnych konsekwencji. Sam tak się opieram. FK dopuszczający wartość null reprezentuje relację opcjonalną. W przypadkach, w których jednostka nie ma relacji, FK zawiera NULL. Przestrzeń nad głową jest minimalna. Kiedy łączenia (dokładniej equijoins) są wykonywane w dwóch tabelach, instancje zawierające NULL w FK wypadną z łączenia, i to jest właściwe.
Powiedziawszy to, mam zamiar polecić ci czwartą metodę. Obejmuje to łącznie 4 tabele, konta, widżety, typy i custom_types. Tabela custom_types wykorzystuje technikę o nazwie Shared-primary-key, opisaną poniżej.
CREATE TABLE accounts (
account_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
# Other Columns...,
PRIMARY KEY (account_id)
);
CREATE TABLE widgets (
widget_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
account_id INT UNSIGNED NOT NULL,
type_id INT UNSIGNED NOT NULL,
PRIMARY KEY (widget_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id) ON DELETE CASCADE,
FOREIGN KEY (type_id) REFERENCES types(type_id)
);
CREATE TABLE types (
type_id INT UNSIGNED AUTO_INCREMENT NOT NULL,
account_id INT UNSIGNED NOT NULL,
name VARCHAR(100) NOT NULL,
PRIMARY KEY (type_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id)
CREATE TABLE custom_types (
type_id INT NOT NULL,
account_id INT UNSIGNED NOT NULL,
PRIMARY KEY (type_id),
FOREIGN KEY (type_id) REFERENCES types(type_id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id)
);
Kolumna type_id w custom_types jest udostępnionym kluczem podstawowym. Zauważ, że jest zadeklarowany ZARÓWNO jako klucz podstawowy, jak i jako klucz obcy, i że nie używa autonumerowania. Jest to kopia klucza podstawowego w typach dla odpowiedniego wpisu. Tabela typów niestandardowych zawiera wszystkie dane, które są obecne w typach niestandardowych, ale nie występują w typach predefiniowanych.
W przypadku gotowych typów wpis jest dokonywany w types, ale nie ma wpisu w custom_types. W przypadku custom_types wpis jest najpierw dokonywany w typach, a następnie wynikowa wartość type_id jest kopiowana do custom_types wraz z account_id.
Jeśli używasz INNER JOIN typów i custom_types, predefiniowane typy wypadają z łączenia. Jeśli chcesz, aby w jednym złączeniu znajdowały się zarówno typy niestandardowe, jak i gotowe, musisz użyć LEFT JOIN lub RIGHT JOIN, aby uzyskać ten efekt. Zauważ, że wynik LEFT lub RIGHT JOIN będzie zawierał kilka wartości NULL, nawet jeśli te wartości NULL nie są przechowywane w bazie danych.
Kliknięcie tego shared-primary-key poda bardziej szczegółowy opis techniki współdzielonego klucza podstawowego.