Pregunta

Estoy trabajando en una serie de aplicaciones Delphi que necesitarán actualizar sus propias estructuras de bases de datos en el campo cuando se lancen nuevas versiones y cuando los usuarios decidan instalar módulos adicionales.Las aplicaciones utilizan una variedad de bases de datos integradas (DBISAM y Jet actualmente, pero esto puede cambiar).

En el pasado hice esto con DBISAM usando los números de versión del usuario que se pueden almacenar con cada tabla.Envié un conjunto adicional y vacío de archivos de base de datos y, al inicio, comparé los números de versión de cada tabla usando FieldDefs para actualizar la tabla instalada si era necesario.Si bien esto funcionó, me resultó complicado tener que enviar una copia de repuesto de la base de datos y las versiones más recientes de DBISAM han cambiado la metodología de reestructuración de tablas, por lo que tendré que reescribir esto de todos modos.

Puedo ver dos formas de implementar esto:almacenar un número de versión con la base de datos y usar scripts DDL para pasar de versiones anteriores a versiones más nuevas o almacenar una versión de referencia de la estructura de la base de datos dentro de la aplicación, comparar la referencia con la base de datos al iniciar y hacer que la aplicación genere comandos DDL para actualizar la base de datos.

Creo que probablemente tendré que implementar partes de ambos.No querré que la aplicación diferencie la base de datos con la estructura de referencia cada vez que se inicie (demasiado lento), por lo que necesitaré un número de versión de la estructura de la base de datos para detectar si el usuario está utilizando una estructura desactualizada.Sin embargo, no estoy seguro de poder confiar en scripts preescritos para realizar la actualización estructural cuando la base de datos podría haberse actualizado parcialmente en el pasado o cuando el usuario haya cambiado la estructura de la base de datos, por lo que me inclino a usar un diferencia de referencia para la actualización real.

Al investigar la pregunta, encontré un par de herramientas de control de versiones de bases de datos, pero todas parecen estar dirigidas a SQL Server y están implementadas fuera de la aplicación real.Estoy buscando un proceso que esté estrechamente integrado en mi aplicación y que pueda adaptarse a diferentes requisitos de bases de datos (sé que tendré que escribir adaptadores, clases descendientes personalizadas o código de evento para manejar las diferencias en DDL para varios bases de datos, eso no me molesta).

¿Alguien sabe de algo disponible que haga esto o, en su defecto, alguien tiene alguna idea sobre:

  1. La mejor manera de almacenar una versión de referencia de una estructura de base de datos relacional genérica dentro de una aplicación.

  2. La mejor manera de comparar la referencia con la base de datos real.

  3. La mejor forma de generar DDL para actualizar la base de datos.

¿Fue útil?

Solución

Tengo una publicación de blog aquí sobre cómo hago dbisam versionado de bases de datos y servidor sql .

Las partes importantes son:

  

Debido a que dbisam no admite vistas,   el número de versión se almacena (junto   con un montón de otra información) en un ini   archivo en el directorio de la base de datos.

     

Tengo un módulo de datos,   TdmodCheckDatabase. Esto tiene un   Componente TdbisamTable para cada tabla   en la base de datos El componente de la tabla   contiene todos los campos en la tabla y   se actualiza cada vez que la tabla es   cambiado.

     

Para realizar cambios en la base de datos, el   Se utilizó el siguiente proceso:

     
      
  1. Aumente el número de versión en la aplicación
  2.   
  3. Realizar y probar cambios en la base de datos.
  4.   
  5. Actualizar las tablas afectadas en TdmodCheckDatabase
  6.   
  7. Si es necesario (rara vez) agregue más consultas de actualización a   TdmodCheckDatabase. P.ej. para configurar el   valores de nuevos campos, o para agregar nuevos   filas de datos.
  8.   
  9. Genere un script de unidad CreateDatabase utilizando la base de datos suministrada   herramientas.
  10.   
  11. Actualice las pruebas unitarias para adaptarlas al nuevo db
  12.   
     

Cuando se ejecuta la aplicación, se va   a través del siguiente proceso

     
      
  1. Si no se encuentra una base de datos, ejecute la unidad CreateDatabase y luego haga   paso 3
  2.   
  3. Obtenga el número de versión actual del archivo ini de la base de datos
  4.   
  5. Si es menor que el número de versión esperado, entonces     Ejecute CreateDatabase (para crear nuevas tablas)     Verifique cada componente de la tabla en TdmodCheckDatabase     Aplicar cualquier cambio de tabla     ejecutar cualquier script de actualización manual
  6.   
  7. Actualice el número de versión en el archivo ini de la base de datos
  8.   

Un ejemplo de código es

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;

Otros consejos

Historia similar aquí. Almacenamos un número de versión de DB en una tabla de 'sistema' y lo verificamos al inicio. (¡Si la tabla / campo / valor no existe, entonces sabemos que es la versión 0 donde olvidamos agregar ese bit!)

Durante el desarrollo a medida que necesitamos actualizar la base de datos, escribimos un script DDL para hacer el trabajo y, una vez contentos de que funciona, se agrega como recurso de texto a la aplicación.

Cuando la aplicación determina que necesita actualizar, carga los recursos apropiados y los ejecuta. Si necesita actualizar varias versiones, debe ejecutar cada secuencia de comandos en orden. Resulta ser solo unas pocas líneas de código al final.

El punto principal es que en lugar de usar las herramientas basadas en GUI para modificar tablas de manera ad-hoc o 'aleatoria', en realidad escribimos el DDL de inmediato. Esto hace que sea mucho más fácil, cuando llegue el momento, construir el script de actualización completo. Y no se requiere diferenciación de estructura.

Estoy usando ADO para mis bases de datos. También uso un esquema de número de versión, pero solo como un control de cordura. Tengo un programa que desarrollé que usa Connection.GetTableNames y Connection.GetFieldNames para identificar cualquier discrepancia con un documento XML que describe el & Quot; master & Quot; base de datos. Si hay una discrepancia, entonces construyo el SQL apropiado para crear los campos faltantes. Nunca dejo caer otros adicionales.

Entonces tengo una tabla dbpatch, que contiene una lista de parches identificados por un nombre único. Si faltan parches específicos, se aplican y se agrega el registro apropiado a la tabla dbpatch. La mayoría de las veces se trata de nuevos procesos almacenados, o cambio de tamaño de campo o índices

También mantengo una versión min-db, que también se verifica, ya que permito que los usuarios usen una versión anterior del cliente, solo les permito usar una versión que sea > = min-db- version y < = cur-db-version.

Lo que hago es almacenar un número de versión en la base de datos y un número de versión en la aplicación. Cada vez que necesito cambiar la estructura de la base de datos, creo un código para actualizar la estructura de la base de datos y aumentar el número de versión en la aplicación. Cuando se inicia la aplicación, compara, numera y, si es necesario, ejecuta un código para actualizar la estructura de la base de datos Y actualiza el número de versión de la base de datos. Por lo tanto, la base de datos ahora está actualizada con la aplicación. Mi código es algo así como

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 solo ejecuta el código necesario algo así como:

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

Puede usar el mismo código para crear la base de datos desde cero

CreateDatabase;
for i := 1 to AppVersion do
  UpdateStructure(i);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top