Modello di progettazione - Una delle tante tabelle principali


13

Mi imbatto in una situazione nel database abbastanza frequentemente in cui una determinata tabella può essere utilizzata da una delle diverse tabelle principali. Ho visto due soluzioni al problema, ma nessuna delle due è soddisfacente. Sono curioso di sapere quali altri schemi hai visto là fuori? C'è un modo migliore per farlo?

Un esempio contrastato
Diciamo che il mio sistema ha Alerts. Gli avvisi possono essere ricevuti per una varietà di oggetti: clienti, notizie e prodotti. Un determinato avviso può essere per un solo elemento. Per qualsiasi motivo, clienti, articoli e prodotti si spostano rapidamente (o localizzati), pertanto il testo / i dati necessari non possono essere inseriti negli avvisi al momento della creazione di un avviso. Data questa configurazione, ho visto due soluzioni.

Nota: sotto DDL è per SQL Server ma la mia domanda dovrebbe essere applicabile a qualsiasi DBMS.

Soluzione 1 - Più tasti Nullable

In questa soluzione la tabella che collega a una delle tante tabelle ha più colonne FK (per brevità il DDL sottostante non mostra la creazione di FK). IL BUONO - In questa soluzione è bello avere chiavi esterne. La null-optinalità degli FK rende questo comodo e relativamente facile aggiungere dati precisi. THE BAD Querying non è eccezionale perché richiede N LEFT JOINS o N UNION per ottenere i dati associati. In SQL Server, in particolare i JOIN LEFT precludono la creazione di una vista indicizzata.

CREATE TABLE Product (
    ProductID    int identity(1,1) not null,
    CreateUTC    datetime2(7) not null,
     Name        varchar(100) not null
    CONSTRAINT   PK_Product Primary Key CLUSTERED (ProductID)
)
CREATE TABLE Customer (
    CustomerID  int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
     Name       varchar(100) not null
    CONSTRAINT  PK_Customer Primary Key CLUSTERED (CustomerID)
)
CREATE TABLE News (
    NewsID      int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
    Name        varchar(100) not null
    CONSTRAINT  PK_News Primary Key CLUSTERED (NewsID)
)

CREATE TABLE Alert (
    AlertID     int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
    ProductID   int null,
    NewsID      int null,
    CustomerID  int null,
    CONSTRAINT  PK_Alert Primary Key CLUSTERED (AlertID)
)

ALTER TABLE Alert WITH CHECK ADD CONSTRAINT CK_OnlyOneFKAllowed 
CHECK ( 
    (ProductID is not null AND NewsID is     null and CustomerID is     null) OR 
    (ProductID is     null AND NewsID is not null and CustomerID is     null) OR 
    (ProductID is     null AND NewsID is     null and CustomerID is not null) 
)

Soluzione 2: un FK in ciascuna tabella padre
In questa soluzione ogni tabella "padre" ha un FK nella tabella avvisi. Semplifica il recupero degli avvisi associati a un genitore. Sul lato negativo, non esiste una vera catena dall'Allerta a chi fa riferimento. Inoltre, il modello di dati consente avvisi orfani, in cui un avviso non è associato a un prodotto, a una notizia o a un cliente. Ancora una volta, più LEFT JOIN per capire l'associazione.

CREATE TABLE Product (
    ProductID    int identity(1,1) not null,
    CreateUTC    datetime2(7) not null,
     Name        varchar(100) not null
    AlertID     int null,
    CONSTRAINT   PK_Product Primary Key CLUSTERED (ProductID)
)
CREATE TABLE Customer (
    CustomerID  int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
     Name       varchar(100) not null
    AlertID     int null,
    CONSTRAINT  PK_Customer Primary Key CLUSTERED (CustomerID)
)
CREATE TABLE News (
    NewsID      int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
    Name        varchar(100) not null
    AlertID     int null,
    CONSTRAINT  PK_News Primary Key CLUSTERED (NewsID)
)

CREATE TABLE Alert (
    AlertID     int identity(1,1) not null,
    CreateUTC   datetime2(7) not null,
    CONSTRAINT  PK_Alert Primary Key CLUSTERED (AlertID)
)

È solo la vita in un database di relazioni? Ci sono soluzioni alternative che hai trovato più soddisfacenti?


1
È possibile creare una tabella principale, modificabile, con le seguenti colonne: ID, CreateDate, Nome, Tipo. Puoi avere il tuo FK da tavolo a tre figli e il tuo ALK attaccherà ad un solo tavolo, Alertable.
AK,

Per alcuni casi hai un buon punto - efficacemente la soluzione n. 3. Ogni volta che i dati vengono spostati o localizzati rapidamente, tuttavia, non funzioneranno. Supponiamo ad esempio che Prodotto, Cliente e Notizie abbiano ciascuno una tabella "Lang" corrispondente per supportare il Nome in diverse lingue. Se devo fornire "nome" nella lingua nativa degli utenti, non posso archiviarlo Alertable. Ha senso?
EBarr,

1
@EBarr: "Se devo fornire" name "nella lingua nativa degli utenti, non posso memorizzarlo in Alertable. Ha senso?" No, non lo fa. Con il tuo schema attuale, se devi fornire il 'nome' nella lingua madre degli utenti, puoi memorizzarlo nella tabella Prodotto, Cliente o Notizie?
ypercubeᵀᴹ

@ypercube Ho aggiunto che a ciascuna di quelle tabelle è associata una tabella delle lingue. Stavo tentando di creare uno scenario in cui il testo "name" può variare in base alla richiesta e quindi non può essere archiviato in Alertable.
EBarr,

A meno che non abbia già un nome accettato, propongo il termine "join polpo" per la query che faresti per visualizzare tutti gli avvisi e i genitori associati. :)
Nathan Long,

Risposte:


4

Capisco la seconda soluzione come non applicabile in quanto non offre una relazione (oggetto) a molti (avviso).

Sei bloccato solo per due soluzioni a causa della rigorosa conformità 3NF.

Progetterei uno schema di accoppiamento minore:

CREATE TABLE Product  (ProductID  int identity(1,1) not null, ...)
CREATE TABLE Customer (CustomerID int identity(1,1) not null, ...)
CREATE TABLE News     (NewsID     int identity(1,1) not null, ...)

CREATE TABLE Alert (
  -- See (1)
  -- AlertID     int identity(1,1) not null,

  AlertClass char(1) not null, -- 'P' - Product, 'C' - Customer, 'N' - News
  ForeignKey int not null,
  CreateUTC  datetime2(7) not null,

  -- See (2)
  CONSTRAINT  PK_Alert Primary Key CLUSTERED (AlertClass, ForeignKey)
)

-- (1) you don't need to specify an ID 'just because'. If it's meaningless, just don't.
-- (2) I do believe in composite keys

Oppure, se la relazione di integrità è obbligatoria, potrei progettare:

CREATE TABLE Product  (ProductID  int identity(1,1) not null, ...)
CREATE TABLE Customer (CustomerID int identity(1,1) not null, ...)
CREATE TABLE News     (NewsID     int identity(1,1) not null, ...)

CREATE TABLE Alert (
  AlertID     int identity(1,1) not null,
  AlertClass char(1) not null, /* 'P' - Product, 'C' - Customer, 'N' - News */
  CreateUTC  datetime2(7) not null,
  CONSTRAINT  PK_Alert Primary Key CLUSTERED (AlertID)
)

CREATE TABLE AlertProduct  (AlertID..., ProductID...,  CONSTRAINT FK_AlertProduct_X_Product(ProductID)    REFERENCES Product)
CREATE TABLE AlertCustomer (AlertID..., CustomerID..., CONSTRAINT FK_AlertCustomer_X_Customer(CustomerID) REFERENCES Customer)
CREATE TABLE AlertNews     (AlertID..., NewsID...,     CONSTRAINT FK_AlertNews_X_News(NewsID)             REFERENCES News)

Comunque...

Tre soluzioni valide più un'altra da prendere in considerazione per molte relazioni (oggetti) -uno-uno (avviso) ...

Questi presentati, qual è la morale?

Differiscono sottilmente e pesano allo stesso modo sui criteri:

  • prestazioni su inserimenti e aggiornamenti
  • complessità nelle query
  • spazio di archiviazione

Quindi, scegli quello più comodo per te.


1
Grazie per l'input; Sono d'accordo con la maggior parte dei tuoi commenti. Probabilmente ho descritto inavvertitamente la relazione richiesta (esclusa la soluzione 2 ai tuoi occhi), in quanto era un esempio inventato. fn1 - capito, stavo solo semplificando molto i tavoli per concentrarmi sul problema. fn2 - tasti compositi e torno indietro! Per quanto riguarda lo schema di accoppiamento minore, capisco la semplicità, ma provo personalmente a progettare con DRI ove possibile.
EBarr,

Revisionando questo, ho iniziato a dubitare della correttezza della mia soluzione ... comunque, è stato votato due volte, che ringrazio. Anche se credo che il mio progetto sia valido ma non adatto al problema dato, in quanto le 'n' unioni / join non vengono affrontate ...
Marcus Vinicius Pompeu

L'acronimo DRI mi ha preso. Per tutti, sta per integrità referenziale dichiarativa, la tecnica alla base dell'integrità dei dati referenziali che è comunemente implementata come ... (rullo di tamburi) ... DDL FOREIGN KEY. Altro su en.wikipedia.org/wiki/Declarative_Referential_Integrity e msdn.microsoft.com/en-us/library/…
Marcus Vinicius Pompeu

1

Ho usato tabelle di join gestite da trigger. la soluzione funziona piuttosto bene come ultima risorsa se il refactoring del db non è possibile o desiderabile. L'idea è che hai un tavolo che è lì solo per gestire i problemi del RI e tutto il DRI va contro di esso.

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.