Domanda

Questa domanda è legata allo schema che può essere trovato in una delle mie altre domande qui. Fondamentalmente nel mio database devo conservare gli utenti, località, sensori tra le altre cose. Tutte queste cose sono modificabili nel sistema dagli utenti, e cancellabile.

Tuttavia - quando un elemento viene modificato o eliminato ho bisogno di memorizzare i vecchi dati; Ho bisogno di essere in grado di vedere ciò che i dati è stato prima della modifica.

Ci sono anche elementi non modificabili nel database, come ad esempio "letture". Sono più di un registro davvero. Le letture vengono registrati contro sensori, perché la sua lettura per un sensore particolare.

Se ho generare un rapporto di letture, ho bisogno di essere in grado di vedere ciò che gli attributi per una posizione o un sensore è stato al momento della lettura .

In pratica dovrei essere in grado di ricostruire i dati per qualsiasi punto nel tempo.

Ora, ho fatto questo prima e ottenuto lavorando bene aggiungendo le seguenti colonne per ogni tabella modificabile:

valid_from
valid_to
edited_by

Se valid_to = 9999-12-31 23:59:59 allora questo è il record corrente. Se valid_to uguale VALID_FROM, quindi il record viene eliminato.

Tuttavia, non sono mai stato felice con i trigger avevo bisogno di usare per far rispettare la coerenza chiave esterna.

I può eventualmente evitare di trigger utilizzando l'estensione al database "PostgreSQL". Questo fornisce un tipo di colonna "periodo", che consente di memorizzare un periodo di tempo tra due date, e poi ti permette di fare vincoli CHECK per evitare periodi di sovrapposizione. Che potrebbe essere una risposta.

Mi chiedo però se c'è un altro modo.

Ho visto gente parlare utilizzando le tabelle storiche particolari, ma non mi piace molto l'idea di maintainling 2 tavoli per quasi ogni 1 tavolo (anche se ancora potrebbe essere una possibilità).

Forse ho potuto ridurre la mia implementazione iniziale di non preoccuparsi di verificare la coerenza di record che non sono "corrente" - vale a dire solo fastidio di controllare i vincoli sul record in cui il valid_to è 9999-12-31 23:59:59. Dopotutto, le persone che utilizzano le tabelle storiche non sembrano avere controlli di vincolo su tali tabelle (per la stessa ragione, avreste bisogno di trigger).

Qualcuno ha qualche idea su questo?

PS - il titolo menziona anche base di dati verificabili. Nel sistema precedente ho già detto, c'è sempre il campo edited_by. Questo ha permesso tutte le modifiche di tenere traccia in modo che potessimo sempre vedere chi ha cambiato un record. Non so quanta differenza che potrebbe fare.

Grazie.

È stato utile?

Soluzione

modificato 01 11 gennaio

Ok, quindi c'è un gap tra dove mi siedo (fornire basi di dati completamente verificabili; vostro essendo una particolare esigenza di questo) e dove ci si siede: sulla base di domande e commenti. Il che ci sarà probabilmente allenarsi in commento. Ecco una posizione da cui partire.

  • Per fornire questo requisito, non è necessario a tutti per: trigger; duplicazione di massa; integrità rotto; ecc.

  • Questo non è un requisito temporale classica, sia, in modo che nessun necessità per la capacità di "periodo", ma possono .

  • ValidFrom e validTo è un errore di normalizzazione: il validTo sono dati che può essere facilmente derivati; ValidTo in ogni fila è duplicato, nella ValidFrom della riga successiva; si dispone di un'anomalia di aggiornamento (quando si aggiorna una colonna in una riga, si deve inoltre aggiornare l'altra colonna nella riga successiva); è necessario utilizzare un valore fittizio per "corrente".

    • Tutto inutile, utilizzare solo ValidFrom, e mantenere il db pulito e puro 5NF.

    • L'avvertenza è, se PostgreSQL non può eseguire Sottoquery senza cadere in un mucchio (ala Oracle), allora bene, kep validTo.

Tutte queste cose sono modificabili nel sistema dagli utenti, e cancellabile.

Be ', no. Si tratta di una banca dati in possesso di informazioni importanti; con integrità referenziale, non uno scratchpad, così l'utente può non solo a piedi fino ad esso e "delete" qualcosa. Sarà in contraddizione con l'esigenza stessi utenti di mantenere i dati storici (nella lettura; Alert; Ack; azione; Download).

  • eliminazioni a cascata non sono ammessi. Tali funzioni sono le caselle di controllo per non database, tipi di MS Access. Per i database reali, i vincoli RI fermano genitori con i bambini vengano cancellati.

  • chiavi primarie non può (non deve) essere modificato. Per esempio. ID utente; LocationID; NetworkSlaveCode non cambiano mai; ricordate, sono attentamente considerati identificatori . Una caratteristica di PKs è che sono stabili.

  • È possibile aggiungere nuovi utenti; è possibile modificare una corrente nome dell'utente; ma non è possibile eliminare un utente che ha voci in download, Riconoscimento, Azione.

In sostanza se è modificabile allora deve essere storica (in modo che esclude letture e avvisi).

Inoltre non comprende: Download; Ringraziamenti; Azioni.

E le tabelle di riferimento: SensorType; AlertType; ActionType.

E le nuove tabelle. Storia: sono inseriti in, ma non possono essere aggiornate o eliminati

Il problema che ho trovato con la bandiera isObselete è .. Dire se si modifica la posizione, la chiave esterna sensore sarà ora punto a un record obsoleto, il che significa che sarà necessario duplicare ogni record sensore. Questo problema diventa esponenziale peggio, come la gerarchia diventa più grande.

  • Ok, ora hai capito il LocationId (FK) in Sensor non cambierà; non v'è alcuna duplicazione di massa, ecc? Non v'è alcun problema in primo luogo (e non v'è in quel libro stupido!) Che ottiene esponenzialmente peggiore nel secondo posto.

  • IsObsolete è inadeguato per il vostro requisito. (vedere sotto)

  • Il UpdatedDtm in una vera e propria fila (Reading, ecc) identifica la Capogruppo (FK a Sensor) fila Storia (il suo AuditedDtm) che era in vigore al momento.

  • Funzionalità full relazionale; Dichiarativa Refential Integrità, ecc.

  • Mantenere l'IDEF1X, concetto relazionale di identificatori forti ... C'è solo una riga padre corrente (ad es. Location)

  • Le righe della Storia sono immagini della riga corrente, prima che fosse cambiato, al AuditedDtm dichiarato. La riga corrente (non la storia) mostra l'ultimo aggiornamentoDDTM, quando la fila è stata modificata.

  • Gli spettacoli AuditedDtm l'intera serie di UpdatedDtms per qualsiasi data chiave; e quindi ho usato per "partizione" la vera chiave in senso temporale.

Tutto ciò che è richiesto è una tabella di storia per ogni tabella mutevole. Ho fornito le tabelle Hiistory per quattro tavoli Identificazione: Posizione; Sensore; NetworkSlave; e l'utente.

Si prega di leggere questo per capire Auditable nel senso contabile .

Data Model

Sensor Data Model con la Storia (pagina 2 contiene le tabelle storia e il contesto).

I lettori che non hanno familiarità con il Relazionale Modeling standard può trovare IDEF1X notazione utile.

risposta ai commenti

(1) Il mio primo problema è quello di integrità referenziale con i dati storici, nel senso che io non sono sicuro che c'è, e se c'è io non sono sicuro di come funziona. Per esempio, in SensoryHistory sarebbe possibile aggiungere un record che ha avuto un UpdatedDtm che indica un tempo data prima della posizione in sé esisteva, se si vede quello che voglio dire. Se questo è in realtà un problema non sono sicuro - far rispettare che potrebbe essere sopra le righe

.

(Si ha sollevato un problema simile in altra questione.) Può essere che i DBS si sono verificati in realtà non hanno l'integrità referenziale in atto; che le linee relazioni erano lì solo per la documentazione; che il RI è stato "implementato nel codice app" (che significa che non c'è RI).

Si tratta di un database di ISO / IEC / ANSI SQL. Ciò consente dichiarativa integrità referenziale. Ogni linea relazione viene implementato come un PK :: FK Riferimento, un vincolo effettivo che viene dichiarato. Ad esempio:

CREATE TABLE Location
    ...
    CONSTRAINT UC_PK
        PRIMARY KEY (LocationId)
    ...
CREATE TABLE Sensor
    ...
    CONSTRAINT UC_PK
        PRIMARY KEY (LocationId, SensorNo)
    CONSTRAINT Location_Sensor_fk
        FOREIGN KEY (LocationId)
        REEFERENCES Location(LocationId)
    ...
CREATE TABLE SensorHistory
    ...
    CONSTRAINT UC_PK
        PRIMARY KEY (LocationId, SensorNo, UpdatedDtm))
    CONSTRAINT Sensor_SensorHistory_fk
        FOREIGN KEY (LocationId, SensorNo)
        REEFERENCES Sensor (LocationId, SensorNo)
    ...
Quei vincoli dichiarati sono applicate dal server; non tramite trigger; Non nel codice app. Ciò significa:

  • Un Sensor con un LocationId che non esiste in Location non può essere inserito
  • Un LocationId in Location che ha righe in Sensor non può essere eliminato
  • Un SensorHistory con un LocationId+SensorNo che non esiste in Sensor non può essere inserito
  • Un LocationId+SensorNo in Sensor che ha righe in SensorHistory non può essere eliminato.

(1.1) Tutte Colonne dovrebbe avere regole e vincoli CHECK per vincolare la loro gamma di valori. Che oltre al fatto che tutti i INSERT / UPDATE / DELETE sono programmatica, all'interno di stored procedure, pertanto, gli incidenti non accadono, e la gente non camminano fino ai comandi del database ed eseguire contro di essa (eccettua seleziona).

In generale ho stare lontano da trigger. Se si utilizza stored procedure, e le autorizzazioni normali, allora questo:

in SensoryHistory sarebbe possibile aggiungere un record che ha avuto un UpdatedDtm che indica un tempo data prima della posizione in sé esistesse, se si vede che cosa voglio dire

è impedito. Quindi, è l'inserimento di un SensorHistory con un'UpdatedDtm prima del sensore stesso. Ma proc non sono regole dichiarative. Tuttavia, se si vuole essere doppiamente sicuro (e intendo doppiamente, perché gli inserti sono tutti tramite un proc, comando diretto dagli utenti), poi certo, è necessario utilizzare un trigger. Per me, che è sopra le righe.

(2) Come faccio indicano la cancellazione? Ho potuto solo aggiungere una bandiera per la versione non-storica della tabella immagino.

Non lo so ancora. Per esempio. Lei accetta che quando un Sensor viene eliminato, è definitiva ... (sì, la storia viene mantenuta)... e poi in caso di nuove Sensor viene aggiunto alla Location, si avrà un nuovo SensorNo ... non c'è Sensor essere logicamente sostituito con quello nuovo, con o senza una lacuna nel tempo?

Dal punto di vista di un utente finale, tramite il software che dovrebbe essere in grado di aggiungere, modificare e sensori di eliminazione a volontà senza alcuna limitazione. Ma sì, ha cancellato una volta viene eliminato e non possono essere eliminate. Non c'è niente per fermarli ri-aggiunta di un sensore più tardi, però con gli esatti stessi parametri.

E Locations, NetworkSlaves "delete", e Users pure.

Ok. Poi il nuovo Sensor con gli stessi parametri, è veramente nuovo, ha un nuovo SensorNo, ed è indipendente da qualsiasi Sensor logica precedente. Siamo in grado di aggiungere un BOOLEAN IsObsolete ai quattro tavoli di identificazione; è ora identificato come adeguata. L'eliminazione è ora un morbido Elimina.

(2.1) Per NetworkSensor e LoggerSensor, che sono in realtà dipende da due genitori: essi sono obsoleti se uno dei loro genitori sono obsolete. Quindi non v'è alcun punto di dare loro una colonna IsObsolete, che ha un duplice significato, che può essere derivato dal genitore applicabile.

(2.2) Giusto per essere chiari, gli utenti non possono eliminare tutte le righe da qualsiasi transazione e le tabelle di storia, giusto?

(3) Quando si aggiorna una tabella, quale metodo sarebbe meglio inserire la nuova riga nella tabella storica e aggiornare la tabella principale? A soli normali istruzioni SQL all'interno di una transazione forse?

Sì. Questo è il classico uso di una transazione, come da proprietà acide, è atomica; riesce sia in toto o non in toto (da ripeterà in seguito quando il problema viene risolto).

(4) con riferimenti a libro

Il testo definitivo e seminale è temporale dei dati e il modello relazionale C J Data, H Darwen, N A Lorentzos. Come in, quelli di noi che abbracciano il RM hanno familiarità con le estensioni, e ciò che è richiesto nel successore del RM; piuttosto che qualche altro metodo.

Il libro si fa riferimento è orribile, e gratuito. Il PDF non è un PDF (nessuna ricerca, senza indicizzazione). Aprendo MS e Oracle sta dicendo; alcuni pezzi buoni espresse in un sacco di lanugine. Molte false dichiarazioni. Non vale la pena rispondere a in dettaglio (se volete una recensione corretta, aprire una nuova domanda).

(4.1) ValidTo oltre a ValidFrom. errore grave (come identificata nella parte superiore della mia risposta), che il libro fa; quindi risolve faticosamente. Non fare l'errore, in primo luogo, e non hai nulla per risolvere in secondo luogo. A quanto mi risulta, che eliminerà i trigger.

(4.2) Regole semplici, prendendo entrambi i requisiti di normalizzazione e temporale in considerazione. In primo luogo, è necessario comprendere a fondo (a) il requisito temporale e (b) i tipi di dati, un uso corretto e limitazioni. Conservare sempre:

  • istantanea come DATETIME, ad es. UpdatedDtm

  • Interval come INTEGER, identificando chiaramente l'unità nel nome della colonna, per esempio. IntervalSec

  • Periodo. Dipende congiunto o disgiunto.

    • Per congiunti, che questo requisito è, (4.1) si applica: uso uno DATETIME; Alla fine del periodo può essere derivato dall'inizio del periodo della riga successiva.
    • Per periodi disgiunti, sì, avete bisogno di 2 x datetimes, ad esempio, RentedFrom e un RentedTo con le lacune nel mezzo.

(4.3) Si scherza con il "Temporal Key primaria", il che complica il codice (oltre a richiedere trigger per controllare l'Anomaly Update). Ho già consegnato un ambiente pulito (provato e testato) Temporal Key primaria.

(4.4) Si scherza con valori fittizi, valori non reali, e Null per "Now". Non permetto queste cose in un database. Dal momento che io non sono la memorizzazione dei ValidTo duplicata, non ho il problema, non c'è nulla da risolvere.

(4.5) Bisogna chiedersi perché una 528 pagina "libro di testo" è disponibile gratuitamente sul web, in formato PDF poveri.

(5) I [un utente] potuto calmare felicemente eliminare tutte le righe LocationHistory per esempio, (lasciando solo la versione corrente nella tabella Location) - anche se può esistere una fila SensorHistory che concettualmente "appartiene" a una versione precedente della posizione, se questo ha un senso.

Non ha senso per me, c'è ancora un gap nella comunicazione dobbiamo chiudere. Si prega di tenere interagire fino a quando non viene chiuso.

    banca dati
  • In un vero e proprio (standard ISO / IEC / ANSI SQL), facciamo non GRANT INSERT / UPDATE / DELETE il permesso agli utenti. Noi GRANT SELECT e RIFERIMENTI solo (per gli utenti scelti) Tutti INSERT / UPDATE / DELETE sono codificati nelle transazioni, quali mezzi memorizzati proc. Poi abbiamo GRANT EXEC su ogni proc memorizzato a utenti selezionati (utilizzare i ruoli di ridurre la somministrazione).

    • Quindi non si può eliminare da qualsiasi tabella senza eseguire un proc.

    • Non scrivere un proc di eliminare da qualsiasi tabella di storia. Queste righe non devono essere cancellati. In questo caso, il non-permesso e l'inesistenza di codice il vincolo.

    • Tecnicamente, tutte le righe di storia sono validi, non c'è periodo di preoccupazione per te stesso con. La più antica fila LocationHistory contiene l'immagine prima-della fila posizione originale prima che fosse cambiato. I più giovani righe LocationHistory è la prima immagine della riga posizione corrente. Ogni riga LocationHistory nel mezzo è thusly valido e applicabile al periodo nel mezzo.

    • Non c'è bisogno di "potare" o per cercare un paio di righe LocationHistory che possono essere eliminati sulla base del fatto che si applicano ad un periodo che non viene utilizzato: sono tutti utilizzati . (In definitiva, senza la necessità di verifica di eventuali mappatura di localizzazione figli a qualsiasi riga LocationHistory (s), per dimostrarlo.)

    • In conclusione:. Un utente non può eliminare da qualsiasi tabella di storia (o transazione)

    • Oppure fare ancora vuoi dire qualcosa di diverso?

    • Si noti che ho aggiunto (1.1) di cui sopra.

(6) Corretto un errore nel DM. Un Alert è espressione di Reading, non Sensor.

(7) Corretto le regole di business in altra domanda / risposta per riflettere che; e le nuove regole esposte in questa questione.

(8) Hai capito / apprezzate, che da quando abbiamo un pieno IDEF1X modello conforme alla normativa, re identificatori :

  • Gli identificatori vengono effettuate attraverso l'intero database, mantenendo il loro potere. Per esempio. quando si elencano Acknowledgements, essi possono essere uniti direttamente con Location e Sensor; le tabelle in-tra non devono essere letti (e devono essere se si utilizzano i tasti Id). Questo è il motivo per cui ci sono nei fatti meno unisce richieste in un database relazionale (e più join richiesta in uno denormalizzati).

  • I sottotipi, ecc necessità di essere navigato solo quando quel particolare contesto è rilevante.

Altri suggerimenti

Ho eseguito in questa situazione prima pure. A seconda della quantità di dati che il vostro stanno cercando di tenere traccia di, può essere scoraggiante. La tabella storica funziona bene per la facilità d'uso, a volte perché si può prendere un 'istantanea' del record nella tabella della cronologia, quindi apportare le modifiche necessarie nella tabella di produzione. E 'piuttosto semplice da implementare, ma a seconda della quantità di dati che hai e quanto spesso cambia, si può finire con tabelle molto grandi storici.

Un'altra opzione sta registrando tutte le modifiche che permettono a qualcuno di 'Replay' quello che è successo e tenere traccia di esso. Ogni modifica viene registrato in una tabella o un campo (a seconda delle esigenze), che tiene traccia di chi, quando e cosa è stato modificato a quello che vale a dire Al 31 Dicembre 2010 Bob ha cambiato lo stato da 'Open' per 'chiuso'.

Quale sistema che si desidera utilizzare di solito dipende necessità come you'l mantenere / recensione / utilizzare i dati in seguito. report automatici, recensione di una persona, una combinazione dei due, ecc.

A seconda del budget e / o l'ambiente si potrebbe desiderare di considerare l'utilizzo di funzionalità di archiviazione flashback di Oracle.

È possibile attivare automatico "archiviazione" di righe di una tabella, e quindi eseguire una dichiarazione sulla BaseTable utilizzando qualcosa di simile

SELECT *
FROM important_data
AS OF TIMESTAMP (SYSTIMESTAMP - INTERVAL '5' DAY)

Oracle si prende cura di mantenere la storia in un (shadow) tabella separata. Si può fare questo per qualsiasi tabella in modo che si può anche fare una query con un join.

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