Domanda

Sto lavorando su una serie di applicazioni Delphi che dovranno aggiornare le proprie strutture di database sul campo quando vengono rilasciate nuove versioni e quando gli utenti scelgono di installare moduli aggiuntivi. Le applicazioni utilizzano una varietà di database incorporati (DBISAM e Jet attualmente, ma questo potrebbe cambiare).

In passato l'ho fatto con DBISAM usando i numeri di versione dell'utente che possono essere memorizzati con ogni tabella. Ho spedito un set aggiuntivo e vuoto di file di database e, all'avvio, ho confrontato i numeri di versione di ogni tabella usando FieldDefs per aggiornare la tabella installata, se necessario. Mentre questo ha funzionato, ho trovato goffo dover spedire una copia di riserva del database e le versioni più recenti di DBISAM hanno cambiato la metodologia di ristrutturazione della tabella, quindi dovrò riscriverla comunque.

Posso vedere due modi per implementarlo: archiviare un numero di versione con il database e usare gli script DDL per passare da versioni precedenti a versioni più recenti o archiviare una versione di riferimento della struttura del database all'interno dell'applicazione, confrontando il riferimento al database all'avvio e facendo in modo che l'applicazione generi comandi DDL per aggiornare il database.

Penso che probabilmente dovrò implementare parti di entrambi. Non voglio che l'applicazione differisca il database dalla struttura di riferimento ogni volta che l'applicazione viene avviata (troppo lentamente), quindi avrò bisogno di un numero di versione della struttura del database per rilevare se l'utente sta utilizzando una struttura obsoleta. Tuttavia, non sono sicuro di potermi fidare degli script pre-scritti per eseguire l'aggiornamento strutturale quando il database potrebbe essere stato parzialmente aggiornato in passato o quando l'utente potrebbe aver modificato la struttura del database, quindi sono propenso a utilizzare un diff di riferimento per l'aggiornamento effettivo.

Ricerca della domanda Ho trovato un paio di strumenti per il controllo delle versioni del database, ma sembrano tutti indirizzati verso SQL Server e implementati al di fuori dell'applicazione reale. Sto cercando un processo che sarebbe strettamente integrato nella mia applicazione e che potrebbe essere adattato ai diversi requisiti del database (so che dovrò scrivere adattatori, classi discendenti personalizzate o codice evento per gestire le differenze in DDL per vari database, questo non mi disturba).

Qualcuno sa qualcosa che non lo fa o fallisce, qualcuno ha qualche idea su:

  1. Il modo migliore per archiviare una versione di riferimento di una struttura di database relazionale generica all'interno di un'applicazione.

  2. Il modo migliore per diffondere il riferimento rispetto al database effettivo.

  3. Il modo migliore per generare DDL per aggiornare il database.

È stato utile?

Soluzione

Ho un post sul blog qui su come faccio dbisam versione del database e sql server .

Le parti importanti sono:

  

Poiché dbisam non supporta le visualizzazioni,   il numero di versione è memorizzato (insieme   con un sacco di altre informazioni) in un ini   file nella directory del database.

     

Ho un modulo dati,   TdmodCheckDatabase. Questo ha un   Componente TdbisamTable per ogni tabella   nel database. Il componente della tabella   contiene tutti i campi nella tabella e   viene aggiornato ogni volta che la tabella è   cambiato.

     

Per apportare modifiche al database, il   è stato utilizzato il seguente processo:

     
      
  1. Aumenta il numero di versione nell'applicazione
  2.   
  3. Apporta e verifica le modifiche al DB.
  4.   
  5. Aggiorna le tabelle interessate in TdmodCheckDatabase
  6.   
  7. Se necessario (raramente) aggiungi ulteriori query di aggiornamento a   TdmodCheckDatabase. Per esempio. per impostare il   valori di nuovi campi o per aggiungere nuovi   righe di dati.
  8.   
  9. Genera uno script di unità CreateDatabase utilizzando il database fornito   strumenti.
  10.   
  11. Aggiorna i test unitari per adattarli al nuovo db
  12.   
     

Quando l'applicazione viene eseguita, va bene   attraverso la seguente procedura

     
      
  1. Se non viene trovato alcun database, eseguire l'unità CreateDatabase e quindi fare   passaggio 3
  2.   
  3. Ottieni il numero di versione corrente dal file ini del database
  4.   
  5. Se è inferiore al numero di versione previsto, allora     Esegui CreateDatabase (per creare nuove tabelle)     Controllare tutti i componenti della tabella in TdmodCheckDatabase     Applica eventuali modifiche alla tabella     eseguire eventuali script di aggiornamento manuale
  6.   
  7. Aggiorna il numero di versione nel file ini del database
  8.   

Un esempio di codice è

class procedure TdmodCheckDatabase.UpgradeDatabase(databasePath: string; currentVersion, newVersion: integer);
var
module: TdmodCheckDatabase;
f: integer;
begin
module:= TdmodCheckDatabase.create(nil);
try
  module.OpenDatabase( databasePath );

  for f:= 0 to module.ComponentCount -1  do
  begin
    if module.Components[f] is TDBISAMTable then
    begin
      try
        // if we need to upgrade table to dbisam 4
        if currentVersion <= DB_VERSION_FOR_DBISAM4 then
          TDBISAMTable(module.Components[f]).UpgradeTable;

        module.UpgradeTable(TDBISAMTable(module.Components[f]));
      except
       // logging and error stuff removed
      end;
    end;
  end;

  for f:= currentVersion + 1 to newVersion do
    module.RunUpgradeScripts(f);

  module.sqlMakeIndexes.ExecSQL; // have to create additional indexes manually
 finally
  module.DBISAMDatabase1.Close;
  module.free;
end;
end;


procedure TdmodCheckDatabase.UpgradeTable(table: TDBISAMTable);
var
 fieldIndex: integer;
 needsRestructure: boolean;
 canonical: TField;
begin
 needsRestructure:= false;

 table.FieldDefs.Update;

 // add any new fields to the FieldDefs
 if table.FieldDefs.Count < table.FieldCount then
 begin
   for fieldIndex := table.FieldDefs.Count to table.Fields.Count -1 do
   begin
     table.FieldDefs.Add(fieldIndex + 1, table.Fields[fieldIndex].FieldName, table.Fields[fieldIndex].DataType, table.Fields[fieldIndex].Size, table.Fields[fieldIndex].Required);
   end;
   needsRestructure:= true;
 end;

 // make sure we have correct size for string fields
 for fieldIndex := 0 to table.FieldDefs.Count -1 do
 begin
   if (table.FieldDefs[fieldIndex].DataType = ftString) then
   begin
     canonical:= table.FindField(table.FieldDefs[fieldIndex].Name);
     if assigned(canonical) and (table.FieldDefs[fieldIndex].Size <> canonical.Size) then
   begin
     // field size has changed
     needsRestructure:= true;
     table.FieldDefs[fieldIndex].Size:= canonical.Size;
   end;
   end;
 end;

 if needsRestructure then
   table.AlterTable(); // upgrades table using the new FieldDef values
end;

procedure TdmodCheckDatabase.RunUpgradeScripts(newVersion: integer);
begin
 case newVersion of
   3: sqlVersion3.ExecSQL;
   9: sqlVersion9.ExecSQL;
   11: begin  // change to DBISAM 4
         sqlVersion11a.ExecSQL;
         sqlVersion11b.ExecSQL;
         sqlVersion11c.ExecSQL;
         sqlVersion11d.ExecSQL;
         sqlVersion11e.ExecSQL;
       end;
   19: sqlVersion19.ExecSQL;
   20: sqlVersion20.ExecSQL;
 end;
end;

Altri suggerimenti

Storia simile qui. Memorizziamo un numero di versione del DB in una tabella "di sistema" e lo controlliamo all'avvio. (Se la tabella / campo / valore non esiste, allora sappiamo che è la versione 0 in cui ci siamo dimenticati di aggiungere quel bit!)

Durante lo sviluppo come e quando abbiamo bisogno di aggiornare il database, scriviamo uno script DDL per fare il lavoro e una volta felici che funzioni OK, viene aggiunto come risorsa di testo all'app.

Quando l'app determina che deve essere aggiornato, carica le risorse appropriate e le esegue. Se è necessario aggiornare più versioni, è necessario eseguire ogni script in ordine. Alla fine risulta essere solo poche righe di codice.

Il punto principale è che invece di utilizzare gli strumenti basati sulla GUI per modificare le tabelle in modo ad-hoc o 'casuale', in realtà scriviamo subito il DDL. Ciò rende molto più semplice, quando arriva il momento, creare lo script di aggiornamento completo. E non è richiesto il diffing della struttura.

Sto usando ADO per i miei database. Uso anche uno schema del numero di versione, ma solo come controllo di integrità. Ho un programma che ho sviluppato che utilizza Connection.GetTableNames e Connection.GetFieldNames per identificare eventuali discrepanze rispetto a un documento XML che descrive il & Quot; master & Quot; Banca dati. Se c'è una discrepanza, quindi creo l'SQL appropriato per creare i campi mancanti. Non ne butto mai altri.

Ho quindi una tabella dbpatch, che contiene un elenco di patch identificate da un nome univoco. Se mancano patch specifiche, vengono applicate e il record appropriato viene aggiunto alla tabella dbpatch. Molto spesso si tratta di nuovi proc memorizzati, ridimensionamento del campo o indici

Mantengo anche una versione min-db, che è anche verificata poiché consento agli utenti di utilizzare una versione precedente del client, consento loro solo di utilizzare una versione > = min-db- versione e < = cur-db-version.

Quello che faccio è memorizzare un numero di versione nel database e un numero di versione nell'applicazione. Ogni volta che devo modificare la struttura del database, creo un po 'di codice per aggiornare la struttura del database e aumentare il numero di versione nell'applicazione. All'avvio dell'applicazione, confronta i numeri e, se necessario, esegue un codice per aggiornare la struttura del database AND aggiorna il numero di versione del database. Pertanto, il database è ora aggiornato con l'applicazione. Il mio codice è simile a

if DBVersion < AppVersion then
begin
  for i := DBVersion+1 to AppVersion do
    UpdateStructure(i);
end
else
  if DBVersion > AppVersion then
    raise EWrongVersion.Create('Wrong application for this database');

UpdateStructure esegue semplicemente il codice necessario in qualcosa del tipo:

procedure UpdateStructure(const aVersion : Integer);
begin
  case aVersion of
    1 : //some db code
    2 : //some more db code
    ...
    ...
  end;
  UpdateDatabaseVersion(aVersion);
end;  

Puoi effettivamente usare lo stesso codice per creare il database da zero

CreateDatabase;
for i := 1 to AppVersion do
  UpdateStructure(i);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top