sql >> Databáze >  >> RDS >> Sqlserver

Jak vytvořit více jeden k jednomu

Používáte dědičnost (také známou v modelování vztahů mezi entitami jako „podtřída“ nebo „kategorie“). Obecně existují 3 způsoby, jak jej reprezentovat v databázi:

  1. "Všechny třídy v jedné tabulce": Mějte pouze jednu tabulku „pokrývající“ nadřazené a všechny podřízené třídy (tj. se všemi nadřazenými a podřízenými sloupci) s omezením CHECK, aby se zajistilo, že správná podmnožina polí není NULL (tj. dvě různé podřízené položky se „nemíchají“).
  2. "Konkrétní třída na stůl": Mít jiný stůl pro každé dítě, ale žádný rodičovský stůl. To vyžaduje, aby se vztahy rodičů (ve vašem případě Inventář <- Úložiště) opakovaly u všech dětí.
  3. "Třída podle stolu": Mít nadřazený stůl a samostatný stůl pro každé dítě, o což se snažíte. To je nejčistší, ale může to stát určitý výkon (většinou při úpravě dat, ne tolik při dotazování, protože se můžete připojit přímo z potomka a přeskočit rodiče).

Obvykle preferuji 3. přístup, ale prosazuji oba přítomnost a exkluzivita dítěte na aplikační úrovni. Vynucování obojího na úrovni databáze je trochu těžkopádné, ale lze jej provést, pokud DBMS podporuje odložená omezení. Například:

CHECK (
    (
        (VAN_ID IS NOT NULL AND VAN_ID = STORAGE_ID)
        AND WAREHOUSE_ID IS NULL
    )
    OR (
        VAN_ID IS NULL
        AND (WAREHOUSE_ID IS NOT NULL AND WAREHOUSE_ID = STORAGE_ID)
    )
)

Tím se prosadí jak exkluzivita (kvůli CHECK ) a přítomnost (kvůli kombinaci CHECK a FK1 /FK2 ) dítěte.

Bohužel MS SQL Server nepodporuje odložená omezení, ale můžete být schopni "skrýt" celou operaci za uložené procedury a zakázat klientům přímo upravovat tabulky.

Pouze exkluzivitu lze prosadit bez odložených omezení:

STORAGE_TYPE je typový diskriminátor, obvykle celé číslo pro úsporu místa (ve výše uvedeném příkladu jsou 0 a 1 vaší aplikaci „známé“ a podle toho jsou interpretovány).

VAN.STORAGE_TYPE a WAREHOUSE.STORAGE_TYPE lze vypočítat (aka. "vypočítané") sloupce, aby se ušetřilo úložiště a nemuselo se používat CHECK s.

--- UPRAVIT ---

Vypočítané sloupce by fungovaly pod SQL Serverem takto:

CREATE TABLE STORAGE (
    STORAGE_ID int PRIMARY KEY,
    STORAGE_TYPE tinyint NOT NULL,
    UNIQUE (STORAGE_ID, STORAGE_TYPE)
);

CREATE TABLE VAN (
    STORAGE_ID int PRIMARY KEY,
    STORAGE_TYPE AS CAST(0 as tinyint) PERSISTED,
    FOREIGN KEY (STORAGE_ID, STORAGE_TYPE) REFERENCES STORAGE(STORAGE_ID, STORAGE_TYPE)
);

CREATE TABLE WAREHOUSE (
    STORAGE_ID int PRIMARY KEY,
    STORAGE_TYPE AS CAST(1 as tinyint) PERSISTED,
    FOREIGN KEY (STORAGE_ID, STORAGE_TYPE) REFERENCES STORAGE(STORAGE_ID, STORAGE_TYPE)
);

-- We can make a new van.
INSERT INTO STORAGE VALUES (100, 0);
INSERT INTO VAN VALUES (100);

-- But we cannot make it a warehouse too.
INSERT INTO WAREHOUSE VALUES (100);
-- Msg 547, Level 16, State 0, Line 24
-- The INSERT statement conflicted with the FOREIGN KEY constraint "FK__WAREHOUSE__695C9DA1". The conflict occurred in database "master", table "dbo.STORAGE".

Bohužel SQL Server vyžaduje pro vypočítaný sloupec který se používá v cizím klíč být PERSISTED. Jiné databáze nemusí mít toto omezení (např. virtuální sloupce Oracle), což může ušetřit určitý úložný prostor.



  1. Jak mohu vygenerovat jedinečný řetězec na záznam v tabulce v Postgres?

  2. Rychlý příspěvek o SQLite UPSERT a nové klauzuli RETURNING.

  3. SQLITE nemůže upgradovat databázi pouze pro čtení z verze 1 na 2

  4. Vrátit seznam e-mailů odeslaných z SQL Server Database Mail (T-SQL)