it-swarm-eu.dev

Jak udržovat osobní vztah s privilegovaným dítětem?

Chci mít vztah jeden k mnoha, ve kterém je pro každého rodiče jedno nebo nula dětí označena jako „oblíbená“. Ne každý rodič však bude mít dítě. (Mysli na rodiče jako na otázky na tomto webu, děti jako odpovědi a oblíbené jako přijaté odpovědi.) Například

TableA
    Id            INT PRIMARY KEY

TableB
    Id            INT PRIMARY KEY
    Parent        INT NOT NULL FOREIGN KEY REFERENCES TableA.Id

Jak to vidím, mohu do tabulky A přidat následující sloupec:

    FavoriteChild INT NULL FOREIGN KEY REFERENCES TableB.Id

nebo následující sloupec tabulky B:

    IsFavorite    BIT NOT NULL

Problém s prvním přístupem je v tom, že zavádí nulovatelný cizí klíč, který, jak jsem pochopil, není v normalizované podobě. Problém s druhým přístupem spočívá v tom, že je třeba vykonat více práce, aby se zajistilo, že nejoblíbenějším je nejvýše jedno dítě.

Jaká kritéria bych měla použít k určení, jaký přístup použít? Nebo existují i ​​jiné přístupy, které neuvažuji?

Používám SQL Server 2012.

22
cubetwo1729

Jiným způsobem (bez Null a bez cyklů v FOREIGN KEY vztahy) má mít třetí tabulku pro uložení "oblíbených dětí". Ve většině DBMS budete potřebovat další UNIQUE omezení na TableB.

@Aaron byl rychlejší, aby zjistil, že výše uvedená konvence pojmenování je poněkud těžkopádná a může vést k chybám. Obvykle je to lepší (a udrží vás zdravý stav), pokud nemáte ve všech tabulkách Id sloupce a pokud mají sloupce (které jsou spojeny) stejné názvy v mnoha zobrazovaných tabulkách. Zde je přejmenování:

Parent
    ParentID        INT NOT NULL PRIMARY KEY

Child
    ChildID         INT NOT NULL PRIMARY KEY
    ParentID        INT NOT NULL FOREIGN KEY REFERENCES Parent (ParentID)
    UNIQUE (ParentID, ChildID)

FavoriteChild
    ParentID        INT NOT NULL PRIMARY KEY
    ChildID         INT NOT NULL 
    FOREIGN KEY (ParentID, ChildID) 
        REFERENCES Child (ParentID, ChildID)

V SQL-Serveru (který používáte) máte také možnost bitového sloupce IsFavorite, který zmiňujete. Jedinečného oblíbeného dítěte na rodiče lze dosáhnout pomocí filtrovaného jedinečného indexu:

Parent
    ParentID        INT NOT NULL PRIMARY KEY

Child
    ChildID         INT NOT NULL PRIMARY KEY
    ParentID        INT NOT NULL FOREIGN KEY REFERENCES Parent (ParentID)
    IsFavorite      BIT NOT NULL

CREATE UNIQUE INDEX is_FavoriteChild
  ON Child (ParentID)
  WHERE IsFavorite = 1 ;

A hlavním důvodem, proč se vaše volba 1 nedoporučuje, alespoň ne na serveru SQL, je to, že vzor kruhových cest v referencích cizího klíče má určité problémy.

Přečtěte si docela starý článek: SQL By Design: The Circular Reference

Při vkládání nebo mazání řádků ze dvou tabulek narazíte na problém „slepice a vejce“. Kterou tabulku mám vložit jako první - aniž by došlo k porušení jakýchkoli omezení?

Abyste to vyřešili, musíte definovat alespoň jeden sloupec s možností null. (OK, technicky nemusíte, všechny sloupce můžete mít jako NOT NULL ale pouze v DBMS, jako jsou Postgres a Oracle, které implementovaly odložitelná omezení. Viz odpověď @ Erwin v podobné otázce: Komplexní omezení cizího klíče v SQLAlchemy, jak toho lze dosáhnout v Postgresu). Přesto toto nastavení vypadá jako bruslení na tenkém ledu.

Zkontrolujte také téměř totožnou otázku na SO (ale pro MySQL) V SQL, je v pořádku, aby se dvě tabulky vzájemně odkazovaly?, kde moje odpověď je skoro stejná. MySQL však nemá žádné dílčí indexy, takže jedinou životaschopnou možností jsou nulovatelné FK a řešení navíc tabulky.

19
ypercubeᵀᴹ

Záleží na tom, jaká je vaše priorita. Chcete se vyhnout práci nebo chcete dodržovat nejpřísnější pravidla normalizace?

Osobně si myslím, že je lepší mít v dětské tabulce IsFavorite, a byl bych ochoten zapojit se do práce, aby se ujistil, že nejoblíbenějším rodičem je pro každé rodiče maximálně jedno dítě. Primární důvod nemá nic společného s nulovatelnou věcí cizího klíče: Nelíbí se mi myšlenka, že všechny cizí klíče směřují oběma směry.

@ ypercube návrh je také dobrý kompromis.

Kromě toho prosím, prosím, nezavrhujte své schéma nesmyslnými názvy sloupců jako Id. Raději bych viděl, že Id bude pojmenováno smysluplným způsobem v celém schématu. Identifikuje autora? Dobře, říkej to AuthorID. Představuje produkt? Ok, ProductID. Je to zaměstnanec a v některých případech odkazuje na manažera? Ok, EmployeeID a ManagerID mi dávají větší smysl než ID a Parent. I když se to může zdát logické vynechat (a zbytečné to uvést), když začnete psát složitá spojení (nebo zde zadávat dotazy), určitě vycítíte nějaké prokletí, když se pokoušíte zpětně analyzovat spoustu spojení v tomto bodě do sloupců jako a.Parent = b.ID ... blecch.

9
Aaron Bertrand

Data patří do podřízené tabulky. Při spouštění tabulky dbáme na to, aby jeden a jediný záznam byl označen jako oblíbený (nebo v našem případě jako preferovaná adresa).

Myšlenka @ ypercube na samostatnou tabulku je však také dobrá.

1
HLGEM