代码之家  ›  专栏  ›  技术社区  ›  Larry Lustig

使用Delphi在已安装的应用程序中进行数据库版本控制

  •  8
  • Larry Lustig  · 技术社区  · 16 年前

    我正在开发一些Delphi应用程序,当新版本发布时,以及当用户选择安装其他模块时,这些应用程序需要升级自己的数据库结构。这些应用程序正在使用各种嵌入式数据库(DBISAM和Jet目前正在使用,但这种情况可能会改变)。

    在过去,我使用DBISAM完成了这项工作,使用的是用户版本号,而不是每个表可以存储的版本号。我提供了一组额外的空数据库文件,并在启动时使用FieldDefs比较每个表的版本号,以便在必要时更新已安装的表。虽然这样做有效,但我发现必须提供数据库的备用副本是很笨拙的,而且DBISAM的较新版本已经改变了表重构方法,因此无论如何我都需要重写它。

    我可以看到两种实现方法:在数据库中存储版本号,使用DDL脚本从旧版本到新版本,或者在应用程序中存储数据库结构的引用版本,在启动时将引用与数据库进行比较,让应用程序生成DDL命令来升级数据库。

    我想我可能必须实现这两个部分。我不希望应用程序每次启动时都将数据库与引用结构区分开来(太慢),因此我需要一个数据库结构版本号来检测用户是否使用了过时的结构。但是,当数据库在过去可能已经部分更新,或者当用户自己更改了数据库结构时,我不确定是否可以信任预先编写的脚本来执行结构升级,因此我倾向于使用参考差异进行实际更新。

    有没有人知道有什么现成的东西能做到这一点或是不能做到这一点,有没有人想到:

    1. 在应用程序中存储通用关系数据库结构的引用版本的最佳方法。

    2. 将引用与实际数据库区分开来的最佳方法。

    3. 生成DDL以更新数据库的最佳方法。

    4 回复  |  直到 16 年前
        1
  •  2
  •   SeanX    16 年前

    dbisam database versioning sql server .

    重要的部分是:

    因为dbisam不支持视图, 数据库目录中的文件。

    我有一个数据模块, TdmodCheckDatabase。这有一个 在数据库中。表组件 无论何时更新表,都会进行更新

    要更改数据库,请 使用了以下过程:

    1. 增加应用程序中的版本号
    2. 更新TdmodCheckDatabase中受影响的表
    3. 如有必要(很少)将进一步的升级查询添加到 新字段的值,或添加新字段 数据行。
    4. 使用提供的数据库生成CreateDatabase单元脚本
    5. 更新单元测试以适应新的数据库

    通过以下过程

    1. 如果找不到数据库,则运行CreateDatabase单元,然后执行以下操作 步骤3
    2. 如果小于预期的版本号,则 运行CreateDatabase(创建任何新表) 检查TdmodCheckDatabase中的每个表组件
    3. 更新数据库ini文件中的版本号

    下面是一个代码示例

    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;
    
        2
  •  5
  •   shunty    16 年前

    这里也有类似的故事。

    在开发过程中,当我们需要升级数据库时,我们会编写一个DDL脚本来完成这项工作,一旦它正常工作,它就会作为文本资源添加到应用程序中。

    当应用程序确定需要升级时,将加载相应的资源并运行它们。如果需要升级多个版本,则必须按顺序运行每个脚本。结果证明,最后只有几行代码。

        3
  •  2
  •   skamradt    16 年前

    然后我有一个dbpatch表,其中包含一个由唯一名称标识的补丁列表。如果缺少特定的修补程序,则会应用这些修补程序,并将相应的记录添加到dbpatch表中。通常情况下,这是新的存储过程、字段大小调整或索引

    我还维护一个min db版本,该版本也会被检查,因为我允许用户使用旧版本的客户端,我只允许他们使用一个>=最小数据库版本和<=curdb版本。

        4
  •  1
  •   Steve    16 年前

    更新数据库的版本号。因此,数据库现在是最新的应用程序。我的代码类似于

    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');
    

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

    实际上,您可以使用相同的代码从头开始创建数据库

    CreateDatabase;
    for i := 1 to AppVersion do
      UpdateStructure(i);