sql >> Databáze >  >> RDS >> Mysql

Je možné provést cizí klíč MySQL do jedné ze dvou možných tabulek?

To, co popisujete, se nazývá polymorfní asociace. To znamená, že sloupec "cizí klíč" obsahuje hodnotu id, která musí existovat v jedné ze sady cílových tabulek. Obvykle jsou cílové tabulky nějakým způsobem propojeny, jako jsou instance nějaké společné nadtřídy dat. Také byste potřebovali další sloupec vedle sloupce cizího klíče, abyste v každém řádku mohli určit, na kterou cílovou tabulku se odkazuje.

CREATE TABLE popular_places (
  user_id INT NOT NULL,
  place_id INT NOT NULL,
  place_type VARCHAR(10) -- either 'states' or 'countries'
  -- foreign key is not possible
);

Neexistuje způsob, jak modelovat polymorfní asociace pomocí omezení SQL. Omezení cizího klíče vždy odkazuje na jeden cílová tabulka.

Polymorfní asociace jsou podporovány frameworky jako Rails a Hibernate. Ale výslovně říkají, že pro použití této funkce musíte zakázat omezení SQL. Místo toho musí aplikace nebo rámec provést ekvivalentní práci, aby bylo zajištěno, že bude splněna reference. To znamená, že hodnota v cizím klíči je přítomna v jedné z možných cílových tabulek.

Polymorfní asociace jsou slabé s ohledem na vynucování konzistence databáze. Integrita dat závisí na tom, zda všichni klienti přistupují k databázi se stejnou vynucenou logikou referenční integrity a také vynucení musí být bez chyb.

Zde jsou některá alternativní řešení, která využívají referenční integritu vynucenou databází:

Vytvořte jednu extra tabulku pro každý cíl. Například popular_states a popular_countries , který odkazuje na states a countries resp. Každá z těchto „populárních“ tabulek také odkazuje na profil uživatele.

CREATE TABLE popular_states (
  state_id INT NOT NULL,
  user_id  INT NOT NULL,
  PRIMARY KEY(state_id, user_id),
  FOREIGN KEY (state_id) REFERENCES states(state_id),
  FOREIGN KEY (user_id) REFERENCES users(user_id),
);

CREATE TABLE popular_countries (
  country_id INT NOT NULL,
  user_id    INT NOT NULL,
  PRIMARY KEY(country_id, user_id),
  FOREIGN KEY (country_id) REFERENCES countries(country_id),
  FOREIGN KEY (user_id) REFERENCES users(user_id),
);

To znamená, že chcete-li získat všechna oblíbená oblíbená místa uživatele, musíte se dotazovat na obě tyto tabulky. Znamená to však, že se můžete spolehnout na to, že databáze zajistí konzistenci.

Vytvořte places stůl jako supertabulka. Jak Abie zmiňuje, druhou alternativou je, že vaše oblíbená místa odkazují na tabulku jako places , který je rodičem obou states a countries . To znamená, že státy i země mají také cizí klíč k places (dokonce můžete tento cizí klíč nastavit také jako primární klíč states a countries ).

CREATE TABLE popular_areas (
  user_id INT NOT NULL,
  place_id INT NOT NULL,
  PRIMARY KEY (user_id, place_id),
  FOREIGN KEY (place_id) REFERENCES places(place_id)
);

CREATE TABLE states (
  state_id INT NOT NULL PRIMARY KEY,
  FOREIGN KEY (state_id) REFERENCES places(place_id)
);

CREATE TABLE countries (
  country_id INT NOT NULL PRIMARY KEY,
  FOREIGN KEY (country_id) REFERENCES places(place_id)
);

Použijte dva sloupce. Místo jednoho sloupce, který může odkazovat na jednu ze dvou cílových tabulek, použijte dva sloupce. Tyto dva sloupce mohou být NULL; ve skutečnosti by pouze jeden z nich neměl být NULL .

CREATE TABLE popular_areas (
  place_id SERIAL PRIMARY KEY,
  user_id INT NOT NULL,
  state_id INT,
  country_id INT,
  CONSTRAINT UNIQUE (user_id, state_id, country_id), -- UNIQUE permits NULLs
  CONSTRAINT CHECK (state_id IS NOT NULL OR country_id IS NOT NULL),
  FOREIGN KEY (state_id) REFERENCES places(place_id),
  FOREIGN KEY (country_id) REFERENCES places(place_id)
);

Pokud jde o teorii vztahů, polymorfní asociace porušují první normální formu , protože popular_place_id je ve skutečnosti sloupec se dvěma významy:je to buď stát, nebo země. Neuložili byste age osoby a jejich phone_number v jednom sloupci a ze stejného důvodu byste neměli ukládat oba state_id a country_id v jediném sloupci. Skutečnost, že tyto dva atributy mají kompatibilní datové typy, je náhodná; stále znamenají různé logické entity.

Polymorfní asociace také porušují třetí normální formu , protože význam sloupce závisí na zvláštním sloupci, který pojmenovává tabulku, na kterou cizí klíč odkazuje. Ve třetím normálním formuláři musí atribut v tabulce záviset pouze na primárním klíči této tabulky.

Znovu komentář od @SavasVedova:

Nejsem si jistý, zda postupuji podle vašeho popisu, aniž bych viděl definice tabulek nebo příklad dotazu, ale zní to, jako byste prostě měli více Filters tabulky, z nichž každá obsahuje cizí klíč, který odkazuje na centrální Products stůl.

CREATE TABLE Products (
  product_id INT PRIMARY KEY
);

CREATE TABLE FiltersType1 (
  filter_id INT PRIMARY KEY,
  product_id INT NOT NULL,
  FOREIGN KEY (product_id) REFERENCES Products(product_id)
);

CREATE TABLE FiltersType2 (
  filter_id INT  PRIMARY KEY,
  product_id INT NOT NULL,
  FOREIGN KEY (product_id) REFERENCES Products(product_id)
);

...and other filter tables...

Připojení produktů ke konkrétnímu typu filtru je snadné, pokud víte, ke kterému typu se chcete připojit:

SELECT * FROM Products
INNER JOIN FiltersType2 USING (product_id)

Pokud chcete, aby byl typ filtru dynamický, musíte napsat kód aplikace pro vytvoření dotazu SQL. SQL vyžaduje, aby byla tabulka specifikována a opravena v době psaní dotazu. Nemůžete nastavit, aby byla spojená tabulka vybrána dynamicky na základě hodnot nalezených v jednotlivých řádcích Products .

Jedinou další možností je připojit se ke všem filtrovat tabulky pomocí vnějších spojení. Ty, které nemají žádné odpovídající product_id, budou vráceny pouze jako jeden řádek null. Stále však musíte vše napevno zakódovat spojené tabulky, a pokud přidáte nové tabulky filtrů, musíte svůj kód aktualizovat.

SELECT * FROM Products
LEFT OUTER JOIN FiltersType1 USING (product_id)
LEFT OUTER JOIN FiltersType2 USING (product_id)
LEFT OUTER JOIN FiltersType3 USING (product_id)
...

Dalším způsobem, jak se připojit ke všem tabulkám filtrů, je provést to sériově:

SELECT * FROM Product
INNER JOIN FiltersType1 USING (product_id)
UNION ALL
SELECT * FROM Products
INNER JOIN FiltersType2 USING (product_id)
UNION ALL
SELECT * FROM Products
INNER JOIN FiltersType3 USING (product_id)
...

Tento formát však stále vyžaduje, abyste zapsali odkazy na všechny tabulky. To se nedá obejít.



  1. Jak chránit své databáze PostgreSQL před kybernetickými útoky pomocí brány SQL Firewall

  2. Jak nainstalovat postgres s NSIS se všemi parametry?

  3. Je možné specifikovat schéma při připojení k postgres s JDBC?

  4. Reporty Rails nemohou najít sloupec, který tam je