Dibattito sul design:quali sono i modi migliori per archiviare e manipolare oggetti con versione?[Chiuso]

StackOverflow https://stackoverflow.com/questions/11689

Domanda

All'inizio lo lascio intenzionalmente sul vago.Cerco discussioni e quali questioni siano importanti più di quanto cerco risposte difficili.

Sto progettando un'app che faccia qualcosa come la gestione del portafoglio.Il design che ho finora è

  • Problema:un problema che deve essere risolto
  • Soluzione:una proposta di soluzione ad uno o più problemi
  • Relazione:una relazione tra due problemi, due soluzioni o un problema e una soluzione.Ulteriormente suddiviso in:
    • Genitore-figlio: una sorta di categorizzazione/gerarchia ad albero
    • Sovrapposizione: il grado in cui due soluzioni o due problemi affrontano effettivamente lo stesso concetto
    • Indirizzi: il grado in cui un problema indirizza una soluzione

La mia domanda riguarda la natura temporale di queste cose.I problemi emergono e poi svaniscono.Le soluzioni hanno una data di risoluzione prevista, ma questa potrebbe essere modificata man mano che vengono sviluppate.Il grado di una relazione potrebbe cambiare nel tempo man mano che i problemi e le soluzioni si evolvono.

Quindi, la domanda:qual è il miglior design per il controllo delle versioni di queste cose in modo da poter ottenere una prospettiva sia attuale che storica del mio portafoglio?

Dopo:forse dovrei rendere questa domanda più specifica, anche se la risposta di @Eric Beard vale la pena.

Ho preso in considerazione tre progetti di database.Ne parlerò abbastanza di ciascuno per mostrare i loro svantaggi.La mia domanda è:quale scegliere o riesci a pensare a qualcosa di meglio?

1:I problemi (e separatamente le soluzioni) sono autoreferenziali nel controllo delle versioni.

table problems
  int id | string name | text description | datetime created_at | int previous_version_id

  foreign key previous_version_id -> problems.id

Questo è problematico perché ogni volta che voglio una nuova versione, devo duplicare l'intera riga, inclusa quella lunga description colonna.

2:Crea un nuovo tipo di relazione:Versione.

table problems
  int id | string name | text description | datetime created_at

Ciò sposta semplicemente la relazione dalle tabelle Problemi e Soluzioni alla tabella Relazioni.Stesso problema di duplicazione, ma forse un po' più "pulito" dato che ho già un concetto astratto di Relazione.

3:Utilizzare una struttura più simile a Subversion;spostare tutti gli attributi Problema e Soluzione in una tabella separata e versionizzarli.

table problems
  int id

table attributes
  int id | int thing_id | string thing_type | string name | string value | datetime created_at | int previous_version_id

  foreign key (thing_id, thing_type) -> problems.id or solutions.id
  foreign key previous_version_id -> attributes.id

Ciò significa che per caricare la versione corrente di un problema o di una soluzione devo recuperare tutte le versioni dell'attributo, ordinarle per data e quindi utilizzare la più recente.Potrebbe non essere terribile.Ciò che mi sembra davvero negativo è che non riesco a digitare questi attributi nel database.Quello value la colonna deve essere di testo libero.Posso fare il name colonna un riferimento in una colonna separata attribute_names tabella che ha a type colonna, ma non è così forza il tipo corretto in attributes tavolo.

più tardi ancora:risposta ai commenti di @Eric Beard sulle chiavi esterne multitabella:

Ahimè, quello che ho descritto è semplicistico:ci sono solo due tipi di cose (problemi e soluzioni).In realtà ho circa 9 o 10 diversi tipi di cose, quindi avrei 9 o 10 colonne di chiavi esterne nella tua strategia.Volevo utilizzare l'ereditarietà a tabella singola, ma le cose hanno così poco in comune che lo sarebbe estremamente uno spreco combinarli in un'unica tabella.

È stato utile?

Soluzione

Hmm, sembra un po' come questo sito...

Per quanto riguarda la progettazione di un database, un sistema di controllo delle versioni come SVN, in cui non si eseguono mai aggiornamenti, ma si limita a inserire (con un numero di versione) quando le cose cambiano, potrebbe essere ciò di cui hai bisogno.Questo si chiama MVCC, Multi-Value Concurrency Control.Un wiki è un altro buon esempio di questo.

Altri suggerimenti

@Gaio

foreign key (thing_id, thing_type) -> problems.id or solutions.id

Fai attenzione a questo tipo di chiavi esterne "multidirezionali".La mia esperienza ha dimostrato che le prestazioni delle query soffrono notevolmente quando la condizione di unione deve verificare il tipo prima di capire a quale tabella unirsi.Non sembra così elegante ma annullabile

problem_id and solution_id 

funzionerà molto meglio.

Naturalmente, anche le prestazioni delle query risentiranno della progettazione MVCC quando sarà necessario aggiungere il controllo per ottenere la versione più recente di un record.Il compromesso è che non devi mai preoccuparti dei conflitti con gli aggiornamenti.

Come ne pensi:

problemi al tavolo
int id | Nome stringa | Descrizione del testo | DateTime creato_at

tabella problemi_revisioni
revisione int | int id | Nome stringa | Descrizione del testo | DateTime creato_at
ID chiave esterna -> problemi.id

Prima degli aggiornamenti è necessario eseguire un ulteriore inserimento nella tabella revisioni.Questo inserto aggiuntivo è veloce, tuttavia è ciò per cui devi pagare

  1. accesso efficiente alla versione corrente: seleziona i problemi come al solito
  2. uno schema intuitivo e vicino alla realtà che vuoi modellare
  3. i join tra le tabelle nello schema rimangono efficienti
  4. utilizzando un numero di revisione per transazione aziendale è possibile eseguire il controllo delle versioni sui record della tabella come fa SVN sui file.

Suppongo che ci sia

Opzione 4:l'ibrido

Sposta gli attributi Thing comuni in una tabella con ereditarietà singola, quindi aggiungi un file custom_attributes tavolo.Ciò rende le chiavi esterne più semplici, riduce la duplicazione e consente flessibilità.Non risolve i problemi di sicurezza del tipo per gli attributi aggiuntivi.Aggiunge anche un po' di complessità poiché ora ci sono due modi in cui una Cosa può avere un attributo.

Se description e altri campi di grandi dimensioni rimangono nella tabella Things, tuttavia, ciò non risolve il problema dello spazio di duplicazione.

table things
  int id | int type | string name | text description | datetime created_at | other common fields...
  foreign key type -> thing_types.id

table custom_attributes
  int id | int thing_id | string name | string value
  foreign key thing_id -> things.id

È una buona idea scegliere una struttura dati che renda facile rispondere alle domande comuni poste al modello.È molto probabile che tu sia interessato alla posizione attuale per la maggior parte del tempo.A volte, vorrai approfondire la storia per problemi e soluzioni particolari.

Avrei tabelle per problema, soluzione e relazione che rappresentano la posizione corrente.Ci sarebbe anche un problem_history, solution_history, tabella, ecc.Queste sarebbero tabelle figlie del problema ma conterrebbero anche colonne aggiuntive per VersionNumber E EffectiveDate.La chiave sarebbe (ProblemId, VersionNumber).

Quando aggiorni un problema, dovresti scrivere i vecchi valori nel file problem_history tavolo.Sono quindi possibili query puntuali in quanto è possibile selezionare il file problem_history record valido a una data particolare.

Laddove l'ho già fatto in precedenza, ho anche creato una vista su UNION problem E problem_history poiché questo a volte è utile in varie query.

L'opzione 1 rende difficile interrogarsi sulla situazione attuale, poiché tutti i dati storici sono mescolati con i dati attuali.

L'opzione 3 sarà dannosa per le prestazioni delle query e sarà difficile da codificare poiché accederai a molte righe per quella che dovrebbe essere solo una semplice query.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top