it-swarm.dev

Jak odwzorować relację IS-A w bazie danych?

Rozważ następujące:

entity User
{
    autoincrement uid;
    string(20) name;
    int privilegeLevel;
}

entity DirectLoginUser
{
    inherits User;
    string(20) username;
    string(16) passwordHash;
}

entity OpenIdUser
{
    inherits User;
    //Whatever attributes OpenID needs... I don't know; this is hypothetical
}

Różne rodzaje użytkowników (użytkownicy z bezpośrednim logowaniem i użytkownicy OpenID) wykazują zależność IS-A; mianowicie, że oba typy użytkowników są użytkownikami. Istnieje kilka sposobów przedstawienia tego w RDBMS:

Way One

CREATE TABLE Users
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privlegeLevel INTEGER NOT NULL,
    type ENUM("DirectLogin", "OpenID") NOT NULL,
    username VARCHAR(20) NULL,
    passwordHash VARCHAR(20) NULL,
    //OpenID Attributes
    PRIMARY_KEY(uid)
)

Drugi sposób

CREATE TABLE Users
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privilegeLevel INTEGER NOT NULL,
    type ENUM("DirectLogin", "OpenID") NOT NULL,
    PRIMARY_KEY(uid)
)

CREATE TABLE DirectLogins
(
    uid INTEGER NOT_NULL,
    username VARCHAR(20) NOT NULL,
    passwordHash VARCHAR(20) NOT NULL,
    PRIMARY_KEY(uid),
    FORIGEN_KEY (uid) REFERENCES Users.uid
)

CREATE TABLE OpenIDLogins
(
    uid INTEGER NOT_NULL,
    // ...
    PRIMARY_KEY(uid),
    FORIGEN_KEY (uid) REFERENCES Users.uid
)

Sposób trzeci

CREATE TABLE DirectLoginUsers
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privlegeLevel INTEGER NOT NULL,
    username VARCHAR(20) NOT NULL,
    passwordHash VARCHAR(20) NOT NULL,
    PRIMARY_KEY(uid)
)

CREATE TABLE OpenIDUsers
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privlegeLevel INTEGER NOT NULL,
    //OpenID Attributes
    PRIMARY_KEY(uid)
)

Jestem prawie pewien, że trzeci sposób jest niewłaściwy, ponieważ nie można wykonać prostego łączenia przeciwko użytkownikom w innym miejscu w bazie danych.

Mój przykład z prawdziwego świata nie jest jednak przykładem użytkowników z różnymi loginami; Interesuje mnie, jak modelować ten związek w ogólnym przypadku.

26
Billy ONeal

Drugi sposób jest prawidłowy.

Twoja klasa podstawowa otrzymuje tabelę, a następnie klasy potomne otrzymują własne tabele z tylko dodatkowymi polami, które wprowadzają, a także odniesienia do klucza obcego do tabeli podstawowej.

Jak Joel zasugerował w swoich komentarzach do tej odpowiedzi, możesz zagwarantować, że użytkownik będzie miał albo login bezpośredni, albo login OpenID, ale nie jedno i drugie (i być może żadne), dodając kolumnę typu do każdej podtyp tabeli, która powraca do tabeli głównej. Kolumna typu w każdej tabeli podtypu jest ograniczona do posiadania pojedynczej wartości reprezentującej typ tej tabeli. Ponieważ ta kolumna ma obcy klucz do tabeli głównej, tylko jeden wiersz podtypu może jednocześnie łączyć się z tym samym wierszem głównym.

Na przykład DDL MySQL wyglądałby mniej więcej tak:

CREATE TABLE Users
(
      uid               INTEGER AUTO_INCREMENT NOT NULL
    , type              ENUM("DirectLogin", "OpenID") NOT NULL
    // ...

    , PRIMARY_KEY(uid)
);

CREATE TABLE DirectLogins
(
      uid               INTEGER NOT_NULL
    , type              ENUM("DirectLogin") NOT NULL
    // ...

    , PRIMARY_KEY(uid)
    , FORIGEN_KEY (uid, type) REFERENCES Users (uid, type)
);

CREATE TABLE OpenIDLogins
(
      uid               INTEGER NOT_NULL
    , type              ENUM("OpenID") NOT NULL
    // ...

    PRIMARY_KEY(uid),
    FORIGEN_KEY (uid, type) REFERENCES Users (uid, type)
);

(Na innych platformach użyłbyś ograniczenia CHECK zamiast ENUM.) MySQL obsługuje złożone klucze obce, więc to powinno działać dla ciebie.

Sposób pierwszy jest prawidłowy, chociaż marnujesz miejsce w tych kolumnach NULL-, ponieważ ich użycie zależy od typu użytkownika. Zaletą jest to, że jeśli zdecydujesz się rozwinąć typy typów użytkowników do przechowywania, a typy te nie wymagają dodatkowych kolumn, możesz po prostu rozwinąć domenę swojego ENUM i użyć tej samej tabeli.

Sposób trzy wymusza wszelkie zapytania, które odsyłają do użytkowników, do sprawdzenia obu tabel. Zapobiega to także odwoływaniu się do tabeli pojedynczych użytkowników za pomocą klucza obcego.

16
Nick Chammas

Zostałyby nazwane

  1. Dziedziczenie pojedynczej tabeli
  2. Dziedziczenie tabeli klas
  3. Dziedziczenie stołu betonowego .

i wszystkie mają swoje legalne zastosowania i są obsługiwane przez niektóre biblioteki. Musisz dowiedzieć się, który najlepiej pasuje.

Posiadanie wielu tabel zwiększyłoby zarządzanie danymi do kodu aplikacji, ale zmniejszyłoby ilość nieużywanego miejsca.

5
flob