Preferisci la normalizzazione del database rispetto alla trasparenza dello schema?


10

Un nuovo requisito è emerso su una vecchia base di codice, che sostanzialmente consente la comunicazione diretta (interna) tra due classi di utenti precedentemente non direttamente collegate (archiviate in tabelle diverse con schema completamente diverso e, purtroppo, il codice è a malapena consapevole di OO, molto meno progettato, quindi non esiste una classe genitore). Dato che siamo pronti a appendere un sacchetto su questa vecchia configurazione che non ha mai considerato questa funzionalità, non vi è alcuna garanzia che non vi siano collisioni PK - dato il set di dati in uso, è praticamente garantito che ci siano ARE.

Quindi, la soluzione sembra ovvia: uccidila con il fuoco e riscrivi l'intero disordine Una tabella di mappatura. Ho ottenuto due indicazioni per i possibili modi di implementare la mappa, ma non sono un DBA, quindi non sono sicuro se ci sono pro e contro che mi sono perso.

Per chiarire l'astrazione, considera tre gruppi di dati utente diversi: Professori, Amministrazione, Studenti (No, questo non è un compito da svolgere a casa. Prometti!)

Mappatura 1

(professor_id, admin_id e student_id sono chiavi esterne delle rispettive tabelle)

| mailing_id (KEY) | professor_id | admin_id | student_id | 
-------------------------------------------------------
| 1001             |     NULL     |    87    |  NULL      |
| 1002             |     123      |   NULL   |  NULL      |
| 1003             |     NULL     |   NULL   |  123       |

Il +/- di questo approccio sembra piuttosto pesante contro:

  • Due campi "sprecati" per riga
  • Viola 2NF
  • Vulnerabile per inserire / aggiornare anomalie (una riga con solo 0-1 campo impostato NULL, ad es.)

I professionisti non sono senza i loro meriti, però:

  • La mappatura può essere eseguita con una singola ricerca
  • Determina facilmente i dati di "origine" per un determinato utente da mailing_id

A dire la verità, nel mio istinto, questa idea non mi piace per niente.

Mappatura 2

(supponiamo che MSG_ * siano costanti definite, tipi di enum o un altro identificatore adatto)

| mailing_id (KEY)  | user_type (UNIQUE1) | internal_id (UNIQUE2)| 
------------------------------------------------------------------
| 1001              | MSG_ADMIN          | 87                    |
| 1002              | MSG_PROF           | 123                   |
| 1003              | MSG_STUDENT        | 123                   |

Con questa configurazione e un indice composito univoco di {user_type, internal_id} le cose diventano molto più pulite, il 3NF viene mantenuto e il codice dell'applicazione non deve verificare le anomalie I / U.

Il rovescio della medaglia, c'è una certa perdita di trasparenza nel determinare le tabelle di origine dell'utente che devono essere gestite al di fuori del DB, sostanzialmente equivalendo a una mappatura a livello di applicazione dei valori user_type sulle tabelle. In questo momento, sono (piuttosto fortemente) incline a questa seconda mappatura, poiché il rovescio della medaglia è piuttosto minore.

MA sono dolorosamente consapevole dei miei limiti e sono sicuro di aver probabilmente perso vantaggi o ostacoli in entrambe le direzioni, quindi mi rivolgo a menti più sagge delle mie.


2
Potresti trovare interessanti le idee di Martin Fowler sui ruoli .
Marjan Venema,

È stato davvero interessante. Purtroppo non troppa comprensione del mio problema specifico
GeminiDomino

Avrai professori che diventano amministratori e studenti che ottengono un lavoro nell'amministrazione o addirittura torneranno 10 anni dopo come docenti. Probabilmente li hai già. Li manterrai separati o proverai a unificare?
Elin,

I ruoli sono solo esempi, ma vedo il tuo punto. In pratica, anche se gli utenti cambiassero ruolo, rimarrebbero comunque come record separati.
GeminiDomino

Sarebbe fantastico se si riformulasse il primo paragrafo. Non è chiaro. Voglio dire, è ovvio che c'è un problema ma non è abbastanza chiaro di cosa si tratti.
Tulains Córdova,

Risposte:


1

La tua seconda idea è quella corretta. Questo approccio consente di eseguire tutte le mappature necessarie per integrare i tre spazi chiave in collisione.

È importante sottolineare che consente al database di imporre la maggior parte della coerenza necessaria per l'utilizzo di vincoli dichiarativi .

Hai già più codice di quello che desideri, quindi non aggiungere più codice di quanto assolutamente necessario per mantenere coerente l'elenco di chiavi integrato. Lascia che il tuo motore di database faccia quello per cui è stato creato.

Il "problema figlio" che ti dà fastidio in Mapping 2 è la USER_TYPEcolonna. Questa colonna è importante perché è necessaria per garantire che venga INTERNAL_IDvisualizzata al massimo una sola volta per tipo di utente. L'unica volta in cui è necessario un codice che sia persino a conoscenza USER_TYPEè il codice che inserisce ed elimina dalla tabella di mappatura. Questo può essere localizzato abbastanza bene. Suppongo che creerai un singolo punto nel codice in cui viene mantenuto il contenuto della tabella di mappatura. Un'ulteriore colonna in questo punto in cui vengono scritti i dati non è un grosso problema. Quello che vuoi davvero evitare è l'aggiunta della colonna aggiuntiva ovunque i dati vengano letti .

Il codice nelle tue sotto-applicazioni che deve usare la mappatura può essere beatamente ignaro del USER_TYPEsemplicemente dando ad ogni sotto-applicazione una vista che filtra le mappature fino al singolo tipo di utente specifico dell'applicazione.


3

Per esperienza, la mia raccomandazione è quella di scegliere la coerenza rispetto all'eleganza o alle "migliori pratiche". Questo è per abbinare il design esistente e andare con TRE tabelle postali (una per ogni ruolo) con una mailing_id, user_idstruttura di campo semplice .

È inelegante ma ha alcuni vantaggi ...

  1. Abbinare la struttura esistente sarà più facile per chiunque lavorerà su questo schema prima che venga messo al pascolo.
  2. Non hai campi sprecati e non stai chiedendo al db di abbinare cose che non esistono.
  3. Perché ogni tabella sarà solo l'una con l'altra e sarà relativamente facile creare una vista che lega tutti i dati da utilizzare per le routine.

Sono sicuro che molti altri non saranno d'accordo con questo approccio, ma gli obiettivi primari della normalizzazione e delle migliori pratiche sono di rendere il codice più coerente, quindi è più facile seguirlo e eseguirne il debug ... e ovviamente portare a zero l'intera base di codice non è probabilmente fattibile.


Il problema con questo approccio è che il database non può quindi applicare l'univocità negli ID di posta, che è lo scopo principale della mappatura in primo luogo: altrimenti, l'associazione dei singoli campi ID da ciascuna tabella, con un indicatore "tipo utente" potrebbe essere fatto senza alcun cambiamento.
GeminiDomino

Vedo che cosa stai ottenendo ma avendo lavorato su quel tipo di sistema ho dato un'opzione che potresti non aver considerato. Per quanto lo vedo, l'id di mailing avrebbe bisogno di alcuni contenuti a cui fare riferimento da qualche parte (cosa è stato inviato o come trovare il documento), quindi l'id di mailing dovrebbe essere una chiave esterna comunque, il che significa che i problemi di unicità sarebbero risolti altrove. Mentre lo leggo, le tabelle di dati dello studente amministratore e del prof che sono collegate possono avere strutture diverse, quindi non riesco a vedere il campo del tipo di utente che aggiunge valore. Gli sviluppatori originali devono aver riscontrato questo problema, cosa hanno fatto?
James Snell,

Il campo "tipo utente" determinerebbe quale tabella associare a quel particolare record. Dovrebbe essere gestito a livello di applicazione in entrambi i modi e poiché si trovano in tabelle diverse, non esiste un buon modo per renderlo un vincolo di chiave esterna. Gli sviluppatori originali non sembrano aver considerato questo problema, sfortunatamente, motivo per cui si sta trasformando in un tale casino. :)
GeminiDomino
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.