Non so come trasformare un'entità variabile in una tabella relazionale


9

INTRODUZIONE E INFORMAZIONI RILEVANTI:

L'esempio seguente illustra il problema che devo affrontare:

L'animale ha una razza, che può essere un gatto o un cane . Il gatto può essere siamese o persiano . Il cane può essere un pastore tedesco o un retriver Labrador .

L'animale è un'entità forte, mentre la sua razza è un attributo che può avere uno dei due valori offerti (gatto o cane). Entrambi questi valori sono complessi (ho aggiunto qui solo il tipo di cane / gatto per illustrare il problema, ma possono esserci anche il nome del gatto / cane e un sacco di altre cose).

PROBLEMA:

Non so come creare tabelle relazionali per questo esempio.

I MIEI EFFETTI PER RISOLVERE IL PROBLEMA:

Ho provato a disegnare un diagramma ER, usando la notazione di Chen, che rappresenta il problema, ma essendo un principiante non so se l'ho fatto bene. Ecco cosa ho:

inserisci qui la descrizione dell'immagine

Mi scuso se ho sbagliato qualcosa, per favore correggimi se è così. Non desidero semplicemente ottenere una "soluzione gratuita", ma anche imparare a gestire questo problema in modo da poterlo risolvere da solo in futuro.

L'unica cosa che mi viene in mente è di creare due tavoli separati, uno per i gatti e uno per i cani. Inoltre, l' attributo razza nella tabella Animale memorizzerebbe solo il valore di gatto o cane . Qualcosa come questo:

Animal< # Animal_ID, race, other attributes >
Cat < # Cat_ID, $ Animal_ID, breed >
Dog < # Dog_ID, $ Animal_ID, breed >

Ho davvero una brutta sensazione riguardo alla mia soluzione e temo che sia sbagliata, quindi la domanda che segue.

DOMANDE:

  • Come posso trasformare il mio esempio in diagramma ER?
  • Come trasformare quel diagramma ER in tabelle relazionali?

Se sono necessarie ulteriori informazioni lascia un commento e aggiornerò il mio post il prima possibile. Inoltre, puoi aggiungere tag appropriati dato che sono abbastanza nuovo qui.

Grazie.


1
La trasformazione dei diagrammi EER in tabelle può essere trovata in questo documento dal 1986 di TJTeorey, D.Yang, JPFry: una metodologia di progettazione logica per database relazionali che utilizzano il modello entità-relazione esteso . È semplice e uno dei miei articoli preferiti.
miracle173,

Risposte:


11

La struttura corretta per questo scenario è un modello di Sottoclasse / Ereditarietà ed è quasi identica al concetto che ho proposto in questa risposta: Elenco di valori ordinato eterogeneo .

Il modello proposto in questa domanda è in realtà abbastanza vicino in quanto l' Animalentità contiene il tipo (cioè race) e le proprietà che sono comuni a tutti i tipi. Tuttavia, sono necessarie due modifiche minori:

  1. Rimuovere i campi Cat_ID e Dog_ID dalle rispettive entità:

    Il concetto chiave qui è che tutto è Animal, indipendentemente da race: Cat, Dog, Elephant, e così via. Dato questo punto di partenza, ogni particolare racedi Animalnon ha davvero bisogno di un identificatore separato poiché:

    1. il Animal_IDè unico
    2. il Cat, Doge tutte le raceentità aggiuntive aggiunte in futuro, da sole, non rappresentano pienamente alcun particolare Animal; hanno solo senso se usato in combinazione con le informazioni contenute nel controllante, Animal.

    Quindi, la Animal_IDproprietà nel Cat, Dogecc entità è sia il PK e la parte posteriore FK alla Animalentità.

  2. Distingue tra tipi di breed:

    Solo perché due proprietà condividono lo stesso nome non significa necessariamente che tali proprietà siano uguali, anche se il nome è lo stesso implica una relazione del genere. In questo caso, quello che hai veramente è in realtà CatBreede DogBreedcome "tipi" separati

Note iniziali

  1. L'SQL è specifico di Microsoft SQL Server (ovvero è T-SQL). Significato, fare attenzione ai tipi di dati in quanto non sono gli stessi in tutti i RDBMS. Ad esempio, sto usando, VARCHARma se hai bisogno di archiviare qualcosa al di fuori del set ASCII standard, dovresti davvero usarlo NVARCHAR.
  2. I campi ID delle tabelle di "tipo" ( Race, CatBreede DogBreed) sono non incremento automatico (cioè IDENTITÀ in termini di T-SQL) perché sono costanti di applicazione (cioè sono parte dell'applicazione) che sono valori di ricerca statiche nel database e sono rappresentati come enumin C # (o altre lingue). Se vengono aggiunti valori, vengono aggiunti in situazioni controllate. Riservo l'uso dei campi di incremento automatico per i dati dell'utente che arrivano tramite l'applicazione.
  3. La convenzione di denominazione che uso è quella di nominare ogni tabella di sottoclasse iniziando con il nome della classe principale seguito dal nome della sottoclasse. Questo aiuta a organizzare le tabelle e indica chiaramente (senza guardare agli FK) la relazione tra la tabella delle sottoclassi e la tabella delle entità principali.
  4. Si prega di consultare la sezione "Modifica finale" alla fine per una nota relativa a Views.

"Razza" come "Gara" - Approccio specifico

Razza come diagramma specifico per razza
Questo primo set di tabelle sono le tabelle di ricerca / tipi:

CREATE TABLE Race
(
  RaceID INT NOT NULL PRIMARY KEY
  RaceName VARCHAR(50) NOT NULL
);

CREATE TABLE CatBreed
(
  CatBreedID INT NOT NULL PRIMARY KEY,
  BreedName VARCHAR(50),
  CatBreedAttribute1 INT,
  CatBreedAttribute2 VARCHAR(10)
  -- other "CatBreed"-specific properties as needed
);

CREATE TABLE DogBreed
(
  DogBreedID INT NOT NULL PRIMARY KEY,
  BreedName VARCHAR(50),
  DogBreedAttribute1 TINYINT
  -- other "DogBreed"-specific properties as needed
);

Questo secondo elenco è l'entità "Animale" principale:

CREATE TABLE Animal
(
  AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  RaceID INT NOT NULL, -- FK to Race
  Name VARCHAR(50)
  -- other "Animal" properties that are shared across "Race" types
);

ALTER TABLE Animal
  ADD CONSTRAINT [FK_Animal_Race]
  FOREIGN KEY (RaceID)
  REFERENCES Race (RaceID);

Questa terza serie di tabelle sono le entità della sottoclasse gratuite che completano la definizione di ciascuna Racedi Animal:

CREATE TABLE AnimalCat
(
  AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
  CatBreedID INT NOT NULL, -- FK to CatBreed
  HairColor VARCHAR(50) NOT NULL
  -- other "Cat"-specific properties as needed
);

ALTER TABLE AnimalCat
  ADD CONSTRAINT [FK_AnimalCat_CatBreed]
  FOREIGN KEY (CatBreedID)
  REFERENCES CatBreed (CatBreedID);

ALTER TABLE AnimalCat
  ADD CONSTRAINT [FK_AnimalCat_Animal]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal (AnimalID);


CREATE TABLE AnimalDog
(
  AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
  DogBreedID INT NOT NULL, -- FK to DogBreed
  HairColor VARCHAR(50) NOT NULL
  -- other "Dog"-specific properties as needed
);

ALTER TABLE AnimalDog
  ADD CONSTRAINT [FK_AnimalDog_DogBreed]
  FOREIGN KEY (DogBreedID)
  REFERENCES DogBreed (DogBreedID);

ALTER TABLE AnimalDog
  ADD CONSTRAINT [FK_AnimalDog_Animal]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal (AnimalID);

Il modello che utilizza un breedtipo condiviso viene visualizzato dopo la sezione "Note aggiuntive".

Note aggiuntive

  1. Il concetto di breedsembra essere un punto focale per la confusione. È stato suggerito da jcolebrand (in un commento sulla domanda) che breedè una proprietà condivisa tra le diverse races, e le altre due risposte l'hanno integrata come tale nei loro modelli. Questo è un errore, tuttavia, perché i valori di breednon sono condivisi tra i diversi valori di race. Sì, sono consapevole che gli altri due modelli proposti tentano di risolvere questo problema creando raceun genitore breed. Sebbene ciò risolva tecnicamente il problema delle relazioni, non aiuta a risolvere la domanda generale di modellazione su cosa fare delle proprietà non comuni, né su come gestire un raceche non ha un breed. Ma, nel caso in cui una tale proprietà fosse garantita per tuttiAnimals, includerò anche un'opzione per quello (sotto).
  2. I modelli proposti da vijayp e DavidN (che sembrano identici) non funzionano perché:
    1. Neanche loro
      1. non consentire la memorizzazione di proprietà non comuni (almeno non per singole istanze di nessuno Animal), oppure
      2. richiedono che tutte le proprietà di tutti gli oggetti racesiano archiviate Animalnell'entità che è un modo molto piatto (e quasi non relazionale) di rappresentare questi dati. Sì, le persone lo fanno sempre, ma significa avere molti campi NULL per riga per le proprietà che non sono pensate per quel particolare raceE sapere quali campi per riga sono associati al particolare racedi quel record.
    2. Essi non consentono l'aggiunta di una racedi Animalin futuro, che non ha breedcome una proprietà. E anche se TUTTI Animalhanno un breed, ciò non cambierebbe la struttura a causa di ciò che è stato precedentemente notato breed: ciò breeddipende dal race(cioè breedper Catnon è la stessa cosa di breedper Dog).

"Razza" come approccio comune / condiviso

inserisci qui la descrizione dell'immagine
Notare che:

  1. L'SQL seguente può essere eseguito nello stesso database del modello presentato sopra:

    1. Il Racetavolo è lo stesso
    2. Il Breedtavolo è nuovo
    3. Le tre Animaltabelle sono state aggiunte con a2
  2. Anche se Breedè una proprietà ormai comune, non sembra giusto non aver Racenotato nell'entità principale / principale (anche se tecnicamente è relazionalmente corretta). Quindi, entrambi RaceIDe BreedIDsono rappresentati in Animal2. Al fine di evitare una discrepanza tra la RaceIDnota in Animal2e una BreedIDche è per un diverso RaceID, ho aggiunto un FK su entrambi RaceID, BreedIDche fa riferimento a UN VINCOLO UNICO di quei campi nella Breedtabella. Di solito disprezzo indicare un FK verso un VINCOLO UNICO, ma ecco uno dei pochi motivi validi per farlo. Una VINCITA UNICA è logicamente una "Chiave alternativa", che la rende valida per questo uso. Si noti inoltre che la Breedtabella ha ancora un PK solo su BreedID.
    1. La ragione per non andare con un solo PK sui campi combinati e nessun VINCOLO UNICO è che permetterebbe BreedIDdi ripetere lo stesso su valori diversi di RaceID.
    2. La ragione per non cambiare il PK e UNICO VINCITORE è che questo potrebbe non essere il solo utilizzo di BreedID, quindi dovrebbe essere ancora possibile fare riferimento a un valore specifico di Breedsenza avere RaceIDdisponibile.
  3. Mentre il seguente modello funziona, ha due potenziali difetti riguardo al concetto di condiviso Breed(e sono il motivo per cui preferisco le tabelle Racespecifiche Breed).
    1. Si presume implicitamente che TUTTI i valori di Breedabbiano le stesse proprietà. In questo modello non esiste un modo semplice per avere proprietà disparate tra Dog"razze" e Elephant"razze". Tuttavia, c'è ancora un modo per farlo, che è annotato nella sezione "Modifica finale".
    2. Non c'è modo di condividere una Breedtra più di una gara. Non sono sicuro che sia desiderabile farlo (o forse non nel concetto di animali, ma forse in altre situazioni che utilizzerebbero questo tipo di modello), ma qui non è possibile.
CREATE TABLE Race
(
  RaceID INT NOT NULL PRIMARY KEY,
  RaceName VARCHAR(50) NOT NULL
);

CREATE TABLE Breed
(
  BreedID INT NOT NULL PRIMARY KEY,
  RaceID INT NOT NULL, -- FK to Race
  BreedName VARCHAR(50)
);

ALTER TABLE Breed
  ADD CONSTRAINT [UQ_Breed]
  UNIQUE (RaceID, BreedID);

ALTER TABLE Breed
  ADD CONSTRAINT [FK_Breed_Race]
  FOREIGN KEY (RaceID)
  REFERENCES Race (RaceID);

CREATE TABLE Animal2
(
  AnimalID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  RaceID INT NOT NULL, -- FK to Race, FK to Breed
  BreedID INT NOT NULL, -- FK to Breed
  Name VARCHAR(50)
  -- other properties common to all "Animal" types
);

ALTER TABLE Animal2
  ADD CONSTRAINT [FK_Animal2_Race]
  FOREIGN KEY (RaceID)
  REFERENCES Race (RaceID);

-- This FK points to the UNIQUE CONSTRAINT on Breed, _not_ to the PK!
ALTER TABLE Animal2
  ADD CONSTRAINT [FK_Animal2_Breed]
  FOREIGN KEY (RaceID, BreedID)
  REFERENCES Breed (RaceID, BreedID);


CREATE TABLE AnimalCat2
(
  AnimalID INT NOT NULL PRIMARY KEY, -- FK to Animal
  HairColor VARCHAR(50) NOT NULL
);

ALTER TABLE AnimalCat2
  ADD CONSTRAINT [FK_AnimalCat2_Animal2]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal2 (AnimalID);

CREATE TABLE AnimalDog2
(
  AnimalID INT NOT NULL PRIMARY KEY,
  HairColor VARCHAR(50) NOT NULL
);

ALTER TABLE AnimalDog2
  ADD CONSTRAINT [FK_AnimalDog2_Animal2]
  FOREIGN KEY (AnimalID)
  REFERENCES Animal2 (AnimalID);


Modifica finale (speriamo ;-)

  1. Per quanto riguarda la possibilità (e quindi la difficoltà) di gestire proprietà disparate tra tipi di Breed, è possibile utilizzare lo stesso concetto di sottoclasse / eredità ma con Breedl'entità principale. In questa configurazione la Breedtabella avrebbe le proprietà comuni a tutti i tipi di Breed(proprio come la Animaltabella) e RaceIDrappresenterebbe il tipo di Breed(come nella Animaltabella). Poi si dovrebbe avere tavoli sottoclassi, come BreedCat, BreedDoge così via. Per i progetti più piccoli questo potrebbe essere considerato "ingegneria eccessiva", ma viene menzionato come un'opzione per le situazioni che ne trarrebbero beneficio.
  2. Per entrambi gli approcci, a volte aiuta a creare viste come scorciatoie per le entità complete. Ad esempio, considera:

    CREATE VIEW Cats AS
       SELECT  an.AnimalID,
               an.RaceID,
               an.Name,
               -- other "Animal" properties that are shared across "Race" types
               cat.CatBreedID,
               cat.HairColor
               -- other "Cat"-specific properties as needed
       FROM    Animal an
       INNER JOIN  AnimalCat cat
               ON  cat.AnimalID = an.AnimalID
       -- maybe add in JOIN(s) and field(s) for "Race" and/or "Breed"
    
  3. Sebbene non facciano parte delle entità logiche, è abbastanza comune avere campi di controllo nelle tabelle per avere almeno un'idea di quando i record vengono inseriti e aggiornati. Quindi in termini pratici:
    1. Un CreatedDatecampo verrebbe aggiunto alla Animaltabella. Questo campo non è necessario in nessuna delle tabelle delle sottoclassi (ad es. AnimalCat) Poiché le righe che vengono inserite per entrambe le tabelle devono essere eseguite contemporaneamente all'interno di una transazione.
    2. Un LastModifiedDatecampo verrebbe aggiunto alla Animaltabella e a tutte le tabelle delle sottoclassi. Questo campo viene aggiornato solo se quella particolare tabella viene aggiornata: se si verifica un aggiornamento AnimalCatma non Animalper un particolare AnimalID, verrà impostato solo il LastModifiedDatecampo in AnimalCat.

2
In qualche modo ho la sensazione che tu abbia capito esattamente quale sia il mio problema. Darò un'occhiata alla tua risposta collegata e la studierò attentamente. Anche una semplice definizione di tabelle sarebbe ottima (se al momento le query SQL sono troppo difficili da scrivere). Se decidi di aggiornare il tuo post con query SQL o definizioni di tabella, lasciami un commento. Grazie ancora. I migliori saluti.
AlwaysLearningNewStuff,

1
Sto cercando di applicare la tua risposta al mio caso di vita reale. Se seguo ciecamente le tue istruzioni, credo che potrei perdere l'occasione di ottimizzare ulteriormente il mio design. Vorrei che tu dessi un'occhiata alla mia ultima domanda poiché hai saputo comprendere perfettamente le mie domande e fornire risposte eccellenti. Ho composto la domanda per utilizzare un modello di dati generico per essere utile anche ai futuri lettori. Se hai problemi a trovarlo lasciami un commento. Grazie e scusa per il disturbo ...
AlwaysLearningNewStuff

@AlwaysLearningNewStuff Hi. Ho ricevuto questo messaggio prima ma non ho avuto il tempo di raggiungerlo immediatamente. Sono stato in grado di trovare la nuova domanda facendo clic sul tuo nome sopra e mostra tutte le tue domande :-).
Solomon Rutzky,

Mi riferivo a questa domanda . In breve: ho 3 entità con attributo comune D, quindi volevo applicare il metodo dalla tua risposta. Due entità hanno un attributo comune Eche non è presente nella terza entità. Devo ignorare questo fatto e applicare la soluzione standard o c'è un modo per ottimizzare ulteriormente il mio design?
AlwaysLearningNewStuff

4

Prima di tutto, stai facendo bene a distinguere tra modellazione ER e modellazione relazionale. Molti neofiti no.

Ecco alcune parole d'ordine che puoi usare per cercare articoli utili sul web.

Il tuo caso è un classico caso di classe / sottoclasse o, se lo desideri, digita / sottotipo.

La frase utilizzata nella modellazione ER è "generalizzazione / specializzazione". E molti articoli lo mostrano sotto qualcosa chiamato modellazione EER (Enhanced Entity-Relationship). Questo non era nella presentazione originale di Peter Chen della modellazione ER. È stato aggiunto più tardi. Per un riassunto abbastanza buono di gen / specifiche in formato pdf, clicca qui

Successivamente, quando si converte un caso di classe / sottoclasse in modellazione relazionale, si progettano tabelle. C'è più di un approccio. I due approcci principali sono chiamati ereditarietà a tabella singola ed ereditarietà delle tabelle di classi. Ognuno ha vantaggi e svantaggi. La migliore presentazione di questi due disegni viene da Martin Fowler. Puoi vedere il suo profilo qui e qui .

Il grande vantaggio dell'ereditarietà di una singola tabella è la semplicità. È tutto memorizzato in una tabella. Il grande svantaggio sono molti NULL. Ciò può sprecare spazio e tempo e provocare una logica confusa.

L'ereditarietà delle tabelle di classi richiede join, ma sono semplici e veloci. Soprattutto se si utilizza una tecnica chiamata chiave primaria condivisa, in cui il PK nelle tabelle delle sottoclassi è una copia del PK nella tabella delle superclassi. È possibile creare viste per ogni sottoclasse che unisce i dati della superclasse con i dati della sottoclasse.

Infine, c'è un tag in quest'area che raccoglie domande come la tua insieme.
Eccolo:


1
+1 Ciò che mi confonde è la mancanza di chiavi primarie nei diagrammi delle tabelle. Soprattutto in "classTableInheritance" non riesco a vedere che tutte queste tabelle sono collegate dalla stessa chiave primaria.
miracle173,

@ miracle173 un punto valido. Per qualche motivo, Fowler non include i PK e gli FK nel diagramma. Esistono altri articoli nell'ereditarietà delle tabelle di classi che forniscono questo dettaglio. Non tutte le implementazioni dell'ereditarietà delle tabelle di classi la combinano con la chiave primaria condivisa. Lo consiglio. È un po 'più di lavoro al momento dell'inserimento, ma più semplice e veloce al momento del recupero unito.
Walter Mitty,

3

Vedo sul possibile design come

tavolo Race

RaceId- PK- Int
RaceName - Varchar(50)

tavolo Breed

BreedId - PK- Int
RaceId - FK - Int
BreedName - varchar(50)

tavolo Animal

AnimalId - PK- Int
BreedId - FK - Int
Other Columns....

Questi PK sopra sarebbero colonna auto-incrementante. Altre colonne nella Animaltabella potrebbero essere nominate di conseguenza.

inserisci qui la descrizione dell'immagine


Inoltre aggiungerei un campo con i tasti Razza e Tipo (potrebbero essere i trigger) nella tabella Animali al fine di facilitare gli indici successivi per migliorare la velocità.
Felipe Alcacibar,

0

Il tuo metodo attuale non è male. Tuttavia, se hai intenzione di aggiungere più razze in seguito (uccelli, pesci, ecc.), Creare una tabella separata per ognuna potrebbe essere complicato. Consiglierei qualcosa di simile al seguente:

Animal < # Animal_ID, Breed_ID, other attributes >
Breed < # Breed_ID, Race_ID >
Race < # Race_ID >

Una razza, per quanto ho capito, dovrebbe avere solo una razza. Quindi, se memorizzi la razza nella tabella Animali, sarai in grado di determinare la razza unendoti alla tabella Razza. Ovviamente, aggiungi qualsiasi altro attributo (nome, descrizione, ecc.) Alle tabelle Razza e Razza secondo necessità.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.