Preferenza di normalizzazione del database rispetto alla trasparenza dello schema?

10

Un nuovo requisito è emerso su una vecchia base di codice, che fondamentalmente consente la comunicazione diretta (interna) tra due classi di utenti precedentemente non direttamente correlate (memorizzate in tabelle diverse con uno schema completamente diverso e, purtroppo, il codice è a malapena OO- consapevole, molto meno progettato, quindi non c'è una classe genitore). Dato che siamo fuori per appendere una borsa su questo vecchio setup che non ha mai considerato questa funzionalità, non c'è alcuna garanzia che non ci siano collisioni PK - dato il set di dati in uso, è praticamente garantito che ci siano.

Quindi, la soluzione sembra ovvia: uccidilo con il fuoco e riscrivi tutto il casino 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 che ci siano pro e contro che ho perso.

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

Mappatura 1

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

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

I +/- a questo approccio sembrano piuttosto pesanti sugli aspetti negativi:

  • Due campi "sprecati" per riga
  • Violates 2NF
  • Vulnerabile per inserire / aggiornare anomalie (una riga con solo 0-1 set di campi 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 il vero, nel mio intimo non mi piace affatto questa idea.

Mappatura 2

(supponiamo che MSG_ * siano costanti definite, tipi 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, 3NF viene mantenuto e il codice dell'applicazione non deve controllare le anomalie I / U.

Sul lato negativo, c'è una certa perdita di trasparenza nel determinare le tabelle di origine degli utenti che devono essere gestite al di fuori del DB, sostanzialmente equivalenti a un mapping a livello di applicazione dei valori user_type alle tabelle. In questo momento, sono (piuttosto strongmente) propenso a questa seconda mappatura, dal momento che il lato negativo è piuttosto minore.

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

    
posta GeminiDomino 31.12.2013 - 05:55
fonte

2 risposte

1

La tua seconda idea è quella corretta. Questo approccio ti consente di fare tutto il mapping che devi fare per integrare i tre spazi chiave in collisione.

È importante sottolineare che consente al database di imporre la maggior parte della coerenza necessaria per utilizzare i vincoli dichiarativi .

Hai già più codice di quello che vuoi avere, 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 ciò per cui è stato realizzato.

Il "problema bambino" che ti dà fastidio in Mappatura 2 è la colonna USER_TYPE . Questa colonna è importante perché è necessaria per garantire che INTERNAL_ID venga visualizzato al massimo una sola volta per tipo di utente. L'unica volta che hai bisogno di un codice che sia a conoscenza di USER_TYPE è il codice che inserisce ed elimina dalla tabella di mapping. Questo può essere localizzato abbastanza bene. Suppongo che creerai un singolo punto nel codice in cui viene mantenuto il contenuto della tabella di mapping. Una colonna in più in questo punto in cui i dati sono scritti non è un grosso problema. Quello che vuoi davvero evitare è aggiungere la colonna in più ovunque i dati sono leggi .

Il codice nelle tue applicazioni secondarie che deve utilizzare la mappatura può ignorare beatamente USER_TYPE semplicemente assegnando a ciascuna sub-applicazione una vista che filtra le mappature fino al tipo di utente specifico di un'applicazione.

    
risposta data 05.08.2016 - 21:16
fonte
3

Per esperienza, la mia raccomandazione è di scegliere la coerenza rispetto all'eleganza o alle "migliori pratiche". Ovvero per abbinare il design esistente e andare con TRE mailing table (una per ogni ruolo) con una semplice struttura di campo mailing_id, user_id .

È inelegante ma ha alcuni vantaggi ...

  1. La corrispondenza con la struttura esistente sarà più semplice per chiunque altrimenti chi lavorerà a questo schema prima che venga messo al pascolo.
  2. Non hai campi sprecati e non stai chiedendo al db di abbinare le cose quello non esisterà.
  3. Perché ogni tavolo sarà solo l'un l'altro e sarà relativamente facile da fare una vista che lega tutti i dati per le routine da usare.

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 ed eseguire il debug ... e ovviamente portare a zero l'intero codebase è probabilmente non fattibile.

    
risposta data 31.12.2013 - 18:19
fonte

Leggi altre domande sui tag