AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Datenbanken Delphi Aktualisieren der DB beim Kunden
Thema durchsuchen
Ansicht
Themen-Optionen

Aktualisieren der DB beim Kunden

Ein Thema von Hobbycoder · begonnen am 10. Feb 2025 · letzter Beitrag vom 11. Feb 2025
Antwort Antwort
Seite 1 von 2  1 2      
Hobbycoder

Registriert seit: 22. Feb 2017
990 Beiträge
 
#1

Aktualisieren der DB beim Kunden

  Alt 10. Feb 2025, 21:56
Datenbank: MariaDB • Version: 11.5.2 • Zugriff über: Zeos 8.0.0
Hi,

ich wollte mal so in die Runde fragen, wie ihr jeweils so die Datenbank beim Kunden aktuell haltet.
Im Zuge der Weiterentwicklung eines Projekts, kommen ja die einen oder anderen Tabellen bzw. Felder in den Tabellen hinzu, oder man ändert auch mal die Feldgrößen.

In der Entwicklungsdatenbank ja keine Problem, jedoch müssen die Änderungen auch zu Kunden übermittelt werden.
In einem älteren Projekt (mysql) habe ich das mal so gemacht, dass ich eine Klasse hatte, die die gesamte Datenbankstruktur abgebildet hat, jedes Feld und jede Tabelle überprüft und ggf. aktualisiert hat. Das hat seinerzeit auch recht gut geklappt.
Jetzt unter MariaDB habe ich das Problem, z.B bei den DECIMAL(10,2) Felder vernüftig an die Größe zu kommen (Sicherlich mein Fehler). Zeos liefert mir den DataType ftFmtBCD zurück, und ich hab noch nicht so ganz raus, wie ich da vernüftig an die Größe kommen. Zwar liefert mir ein in mariadb DECIMAL(10,2) die 2 in Size für die Nachkommastellen, aber wie ich an die 10 komme, da such ich noch.

Aber man könnte ja auch jede DB-Änderung als ALTER TABLE SQL-Zeile speichern und beim Kunden dann abspulen. Ist nur blöd, wenn da mal eine zwischen vergessen wurde. Auch müsste man für die Datenbank eine Version führen, damit die DB soweit aktualisiert wird, auch wenn der Kunde mal ein Update verpasst hat.

Oder könnte man auch ein Script in SQL machen mit Zeilen wie CREATE TABLE IF NOT EXISTS 'blaba' .... ELSE ALTER TABLE 'blabla'... ? (Weiß nicht ob das so geht)

Ich bin mir noch nicht so ganz klar, wie ich es jetzt machen will, damit es später mir möglichst wenig aufwand relativ fehlerfrei funktioniert.
Gruß Hobbycoder
Alle sagten: "Das geht nicht.". Dann kam einer, der wusste das nicht, und hat's einfach gemacht.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.285 Beiträge
 
Delphi 12 Athens
 
#2

AW: Aktualisieren der DB beim Kunden

  Alt 10. Feb 2025, 22:18
Es gibt Milliarden Wege und Möglichkeiten.

So gibt es Diff-Tools für Datenbanken, welche die Strukturen abgleichen.

Bei uns wurden ursprünglich Updatescripte im Setup mitgegeben.
Jetzt liegen die Scripte in einer Tabelle, welche online von einem DB-Server runtergeladen werden.
(mit Versionsnummer und einer optionalen Kunden-Kennung)
Dann wird, entsprechend der aktuellen Programm-Version, ein Versionssetting in der Datenbank hochgesetzt (optional, beim Download, wenn ProgrammVersion neuer als die DBVersion)
und dann werden der Reihe nach die DBUpdates bis zur aktuellen Version (laut Setting) ausgeführt.

Ist aber auch ein Kampf, die DBUpdates und das Erstellungsscript (für neue Datenbanken) synchron zu halten.
Es geht aber nicht nur im die Tabellen-Struktur, Trigger, StoredProcs, sondern auch um geänderte/neue/alte Daten (INSERT/UPDATE/DELETE).
Ein Therapeut entspricht 1024 Gigapeut.
  Mit Zitat antworten Zitat
Papaschlumpf73

Registriert seit: 3. Mär 2014
Ort: Berlin
450 Beiträge
 
Delphi 12 Athens
 
#3

AW: Aktualisieren der DB beim Kunden

  Alt 11. Feb 2025, 08:31
Aber man könnte ja auch jede DB-Änderung als ALTER TABLE SQL-Zeile speichern und beim Kunden dann abspulen. Ist nur blöd, wenn da mal eine zwischen vergessen wurde. Auch müsste man für die Datenbank eine Version führen, damit die DB soweit aktualisiert wird, auch wenn der Kunde mal ein Update verpasst hat.
Das mache ich seit 20 Jahren so und würde ich immer wieder so machen. Stell dir vor, dein Projekt wächst und wächst. Jetzt willst du mal eine Tabelle umbenennen oder neu erstellen und Daten aus einer bisherigen Tabelle reinkopieren. Wenn man jetzt nur die strukturellen Unterschiede ermittelt und behebt, sind die Daten aus der ursprünglichen Tabelle einfach nur weg.
  Mit Zitat antworten Zitat
Benutzerbild von TigerLilly
TigerLilly

Registriert seit: 24. Mai 2017
Ort: Wien, Österreich
1.237 Beiträge
 
Delphi 12 Athens
 
#4

AW: Aktualisieren der DB beim Kunden

  Alt 11. Feb 2025, 09:08
Für mich hat sich bewährt:
- Im Programm gibt es eine Constante DBVersion. Das ist die Version, die das Programm von der Datenbank voraussetzt.
- In der Datenbank gibt es (in einer Tabelle) ebenfalls eine DBVersion, das ist die Version, die die DB tatsächlich hat.
Bei Abweichung: Fehlermeldung.
- Beim Programmstart führt das Programm eine Reihe von solchen Anweisungen aus:
Code:
 Result := Result and DoVersion(nDBVersion, 257, 'ALTER TABLE TBLMATERIAL alter column ' + 'zus_beschreibung nvarchar(max);',
     'Zusatzfelder für Entwicklung II');
Ist die Version lt. DB kleiner als 257 wird das ausgeführt, sonst nicht.
- Bei Erfolg wird die version in der DB angepasst usw
- Weitere Änderungen werden einfach angefügt.
- Das gleiche Schema gibt es BTW auch für Dateninhalte.

Nebennutzen: Da nicht alle wollen, dass die App Änderungen an der DB selber macht, kann man die SQL-Scripts in eine Datei ausgeben lassen, die dann gesondert ausgeführt wird.
Auslassen von Versionsprüngen gibt es damit nicht. Wenn eine Änderung fehlt schlägt fällt es auch sofort auch. Und über den Text bei den Änderungen ergibt sich automatisch eine History/Doku.
Certified Delphi Developer (2025)
  Mit Zitat antworten Zitat
Benutzerbild von MyRealName
MyRealName

Registriert seit: 19. Okt 2003
Ort: Heilbronn
684 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: Aktualisieren der DB beim Kunden

  Alt 11. Feb 2025, 09:57
Ich hatte es damals für Firebird so gebaut (im Moment bin ich in der aktuellen Firma nicht für DB Updates zuständig) :

DB hat Version und Unterversion. Version muss mit der im Programm hinterlegten Version übereinstimmen. Unterversion gibt den Schritt im Updateverlauf an. Ein Schritt konnte das Hinzufügen eines Feldes sein, das Eintragen von neuen Konstanten in eine Tabelle, das Transformieren von Daten über ein "EXECUTE BLOCK" etc. Vorteil war natürlich, dass ich für jeden Vorgang eine spezifische Fehlermeldung hatte, wenn was schief ging und diese speichern und melden konnte, so konnte der Support sich mit dem Kunden pro-aktiv in Verbindung setzen und den Fehler beheben und das Update neu laufen lassen.
Dazu habe ich 2 Tools geschrieben, 1 um die XML Dateien zu erstellen, die die einzelnen Befehle beinhalten und das andere lief beim Kunden, zeigte alle dort registrierten Datenbanken an und in welcher Version diese sind. Dort über Checkbox kurz ausgewählt, welche aktualisiert werden sollen und los ging es
  Mit Zitat antworten Zitat
Frickler
Online

Registriert seit: 6. Mär 2007
Ort: Osnabrück
613 Beiträge
 
Delphi XE6 Enterprise
 
#6

AW: Aktualisieren der DB beim Kunden

  Alt 11. Feb 2025, 11:01
Jede Datenbank hat virtuelle Tabellen mit Metadaten, aus denen man entnehmen kann, wie die Datenbank aufgebaut ist, wie die Tabellen heißen, welche Felder sie haben, welche Typen die haben usw. Die musst Du einfach abfragen, so bekommst Du die benötigten Infos.
Siehe https://mariadb.com/kb/en/informatio...-tables-table/
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.285 Beiträge
 
Delphi 12 Athens
 
#7

AW: Aktualisieren der DB beim Kunden

  Alt 11. Feb 2025, 11:30
Ach ja, bezüglich der AppVersion und DBVersion,
denn es kann ja auch vorkommen, dass
* man mit unterschiedlichen ProgrammVersionen auf die selbe DB zugreift
* oder mit einem Programm auf unterschiedliche Datenbanken.

Für bestimmte Dinge, im Programm, benötigt es eine gewisse DB-Version,
aber für gewisse Funktionen/Strukturen/Daten, benötigt es eine gewisse Programm-Version.
(Minimal-Version und Maximal-Version)

Die Anwendung setzt also voraus, dass die DB eine bestimmte (die gleiche) Version besitzt, bzw. in einem vorgegebenen Versionsbereich vorliegt.
Und/oder andersrum, eine bestimmte DB-Version setzt eine Anwendung vor, welche in einem gewissenen Versionsbereich liegt.
Ein Therapeut entspricht 1024 Gigapeut.

Geändert von himitsu (11. Feb 2025 um 11:32 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von TigerLilly
TigerLilly

Registriert seit: 24. Mai 2017
Ort: Wien, Österreich
1.237 Beiträge
 
Delphi 12 Athens
 
#8

AW: Aktualisieren der DB beim Kunden

  Alt 11. Feb 2025, 15:21
Nachtrag: Wenn man TMS Aurelius als ORM verwendet, dann zieht das ORM die DB automatisch mit.
Certified Delphi Developer (2025)
  Mit Zitat antworten Zitat
Hobbycoder

Registriert seit: 22. Feb 2017
990 Beiträge
 
#9

AW: Aktualisieren der DB beim Kunden

  Alt 11. Feb 2025, 17:45
Danke für die zahlreichen Inspirationen/Informationen.

Jede Datenbank hat virtuelle Tabellen mit Metadaten, aus denen man entnehmen kann, wie die Datenbank aufgebaut ist, wie die Tabellen heißen, welche Felder sie haben, welche Typen die haben usw. Die musst Du einfach abfragen, so bekommst Du die benötigten Infos.
Siehe https://mariadb.com/kb/en/informatio...-tables-table/
Das ist (zumindest für mich) eine richtig gute Idee, weil mir das das Auswerten und verlgeichen der DB supereinfach macht. Mir geht's ja nur darum
- Neue Tabellen zur erzeugen
- Weggefallene Tabellen zu löschen
- Neue Columns hinzufügen
- Weggefallene Columns zu entfernen
- Datetype und Länge der Columns überprüfen und anpassen
- Defaultwert der Columns überprüfen und anpassen

Das konnte ich jetzt mit eine kleine Klasse erledigen, die ich hier mal reinsetze falls das mal irgendwer gebrauchen kann.

Delphi-Quellcode:
unit uTestTables2;

interface

uses System.Classes, System.SysUtils, ZConnection, ZDataset, Data.DB, System.Generics.Collections,
  uRWStream;

type
  TModifyFlag=(mfNoChanges, mfAdd, mfModify, mfDelete);
  TColumnModifyFlag=(cmfNone, cmfDatatype, cmfDefaultValue);

  TDBColumn=class
  private
    FName: string;
    FColumnDefault: string;
    FColumnType: string;
    FModifyFlag: TModifyFlag;
    FColumnModifyFlag: TColumnModifyFlag;
    procedure SetColumnDefault(const Value: string);
    procedure SetColumnType(const Value: string);
    procedure SetName(const Value: string);
    procedure SetModifyFlag(const Value: TModifyFlag);
    procedure SetColumnModifyFlag(const Value: TColumnModifyFlag);
  public
    constructor Create;
    procedure WriteToStream(st: TStream);
    procedure ReadFromStream(st: TStream; Version: Integer);
    procedure Compare(Source: TObject);
    procedure Assign(Source: TObject);
    function GetDefault: string;
  published
    property Name: string read FName write SetName;
    property ColumnType: string read FColumnType write SetColumnType;
    property ColumnDefault: string read FColumnDefault write SetColumnDefault;
    property ModifyFlag: TModifyFlag read FModifyFlag write SetModifyFlag;
    property ColumnModifyFlag: TColumnModifyFlag read FColumnModifyFlag write SetColumnModifyFlag;
  end;

  TDBTable=class(TObjectList<TDBColumn>)
  private
    FName: string;
    FCollation: string;
    FModifyFlag: TModifyFlag;
    procedure SetCollation(const Value: string);
    procedure SetName(const Value: string);
    procedure SetModifyFlag(const Value: TModifyFlag);
  public
    procedure WriteToStream(st: TStream);
    procedure ReadFromStream(st: TStream; Version: Integer);
    procedure Compare(Source: TObject);
    function GetColumnByName(value: string): TDBColumn;
    procedure Assign(Source: TObject);
  published
    property Name: string read FName write SetName;
    property Collation: string read FCollation write SetCollation;
    property ModifyFlag: TModifyFlag read FModifyFlag write SetModifyFlag;
  end;

  TDBDatabase=class(TObjectList<TDBTable>)
  private
    FFilename: string;
    FSchemaName: string;
    FCollation: string;
    procedure SetCollation(const Value: string);
    procedure SetSchemaName(const Value: string);
    function GetSchema(Con: TZConnection; Schema: string): Boolean;
    procedure GetTables(Con: TZConnection; Schema: string);
    procedure GetColumns(Con: TZConnection; Schema: string; TT: TDBTable);
  public
    procedure Compare(Source: TObject);
    procedure BuildFromDB(Con: TZConnection; RootPass: string; Schema: string);
    procedure LoadFromFile(Filename: string = '');
    procedure SaveToFile(Filename: string = '');
    function GetTableByName(value: string): TDBTable;
    function IsModified: Boolean;
    function CreateTableScript(Script: TStrings): Boolean;
    function ExecSQLLine(Con: TZConnection; Line: string): Boolean;
  published
    property SchemaName: string read FSchemaName write SetSchemaName;
    property Collation: string read FCollation write SetCollation;
  end;

Const
  FileVersion = 1;
  StringSeparator = '`';

implementation

{ TDBColumn }

procedure TDBColumn.Assign(Source: TObject);
var
  TC: TDBColumn;
begin
  if Source is TDBColumn then
  begin
    TC:=TDBColumn(Source);
    self.FName:=TC.Name;
    self.FColumnType:=TC.ColumnType;
    self.FColumnDefault:=TC.ColumnDefault;
  end;
end;

procedure TDBColumn.Compare(Source: TObject);
var
  TC: TDBColumn;
begin
  Self.FModifyFlag:=mfNoChanges;
  if Source is TDBColumn then
  begin
    TC:=TDBColumn(Source);
    if (self.FColumnDefault<>TC.ColumnDefault) then
    begin
      Self.FModifyFlag:=mfModify;
      self.FColumnModifyFlag:=cmfDefaultValue;
      self.FColumnDefault:=TC.ColumnDefault;
    end;
    if (self.FColumnType<>TC.ColumnType) then
    begin
      Self.FModifyFlag:=mfModify;
      self.FColumnModifyFlag:=cmfDatatype;
      self.FColumnType:=TC.ColumnType;
    end;
  end;
end;

constructor TDBColumn.Create;
begin
  inherited;
  Self.FColumnModifyFlag:=cmfNone;
end;

function TDBColumn.GetDefault: string;
begin
  if self.FColumnDefault<>'NULLthen
    Result:=''''+self.FColumnDefault+'''else
    Result:=Self.FColumnDefault;
end;

procedure TDBColumn.ReadFromStream(st: TStream; Version: Integer);
begin
  if st<>nil then
  begin
    self.FName:=ReadStrFromStream(st);
    self.FColumnType:=ReadStrFromStream(st);
    self.FColumnDefault:=ReadStrFromStream(st);
  end;
end;

procedure TDBColumn.SetColumnDefault(const Value: string);
begin
  FColumnDefault := Value;
end;

procedure TDBColumn.SetColumnModifyFlag(const Value: TColumnModifyFlag);
begin
  FColumnModifyFlag := Value;
end;

procedure TDBColumn.SetColumnType(const Value: string);
begin
  FColumnType := Value;
end;

procedure TDBColumn.SetModifyFlag(const Value: TModifyFlag);
begin
  FModifyFlag := Value;
end;

procedure TDBColumn.SetName(const Value: string);
begin
  FName := Value;
end;

procedure TDBColumn.WriteToStream(st: TStream);
begin
  if st<>nil then
  begin
    WriteStrToStream(st, self.FName);
    WriteStrToStream(st, self.FColumnType);
    WriteStrToStream(st, self.FColumnDefault);
  end;
end;

{ TDBTable }

procedure TDBTable.Assign(Source: TObject);
var
  TT: TDBTable;
  TC: TDBColumn;
  I: Integer;
begin
  if Source is TDBTable then
  begin
    TT:=TDBTable(Source);
    self.FName:=TT.Name;
    self.FCollation:=TT.Collation;
    self.Clear;
    for I := 0 to TT.Count-1 do
    begin
      TC:=TDBColumn.Create;
      TC.Assign(TT[i]);
      TC.ModifyFlag:=mfAdd;
      self.Add(TC);
    end;
  end;
end;

procedure TDBTable.Compare(Source: TObject);
var
  TT: TDBTable;
  TC, TC2: TDBColumn;
  I: Integer;
begin
  if Source is TDBTable then
  begin
    TT:=TDBTable(Source);
    for I := 0 to Self.Count-1 do
    begin
      TC:=TT.GetColumnByName(self[i].Name);
      if TC<>nil then
        self[i].Compare(TC) else
        self[i].ModifyFlag:=mfDelete;
      if self[i].ModifyFlag<>mfNoChanges then self.FModifyFlag:=mfModify;
    end;
    for I := 0 to TT.Count-1 do
    begin
      TC:=self.GetColumnByName(TT[i].Name);
      if TC=nil then
      begin
        TC2:=TDBColumn.Create;
        TC2.Assign(TC);
        TC2.ModifyFlag:=mfAdd;
        self.Add(TC2);
        self.ModifyFlag:=mfModify;
      end;
    end;
  end;
end;

function TDBTable.GetColumnByName(value: string): TDBColumn;
var
  I: Integer;
begin
  Result:=nil;
  for I := 0 to Self.Count-1 do
    if self[i].Name.ToLower=value.ToLower then
    begin
      Result:=self[i];
      Break;
    end;
end;

procedure TDBTable.ReadFromStream(st: TStream; Version: Integer);
var
  TC: TDBColumn;
  I, C: Integer;
begin
  if st<>nil then
  begin
    Self.Clear;
    self.FName:=ReadStrFromStream(st);
    self.FCollation:=ReadStrFromStream(st);
    C:=ReadIntFromStream(st);
    for I := 0 to C-1 do
    begin
      TC:=TDBColumn.Create;
      TC.ReadFromStream(st, Version);
      self.Add(TC);
    end;
  end;
end;

procedure TDBTable.SetCollation(const Value: string);
begin
  FCollation := Value;
end;

procedure TDBTable.SetModifyFlag(const Value: TModifyFlag);
begin
  FModifyFlag := Value;
end;

procedure TDBTable.SetName(const Value: string);
begin
  FName := Value;
end;

procedure TDBTable.WriteToStream(st: TStream);
var
  I: Integer;
begin
  if st<>nil then
  begin
    WriteStrToStream(st, self.FName);
    WriteStrToStream(st, self.FCollation);
    WriteIntToStream(st, self.Count);
    for I := 0 to self.Count-1 do
      self[i].WriteToStream(st);
  end;
end;

{ TDBDatabase }

procedure TDBDatabase.BuildFromDB(Con: TZConnection; RootPass: string; Schema: string);
var
  FCon: TZConnection;
  FQ: TZQuery;
begin
  FCon:=TZConnection.Create(nil);
  try
    FCon.HostName:=Con.HostName;
    FCon.Port:=Con.Port;
    FCon.User:='root';
    Fcon.Password:=RootPass;
    Fcon.Database:='information_schema';
    Fcon.Protocol:=Con.Protocol;
    Fcon.LibraryLocation:=Con.LibraryLocation;
    Fcon.LibLocation:=Con.LibLocation;
    try
      FCon.Connect;
      if FCon.Connected then
      begin
        self.Clear;
        self.FSchemaName:='';
        self.FCollation:='';
        if self.GetSchema(FCon, Schema) then
        begin
          Self.GetTables(FCon, Schema);
        end;
      end;
    except

    end;
  finally
    FCon.Free;
  end;
end;

procedure TDBDatabase.Compare(Source: TObject);
var
  DB: TDBDatabase;
  TT, TT2: TDBTable;
  I: Integer;
begin
  if Source is TDBDatabase then
  begin
    DB:=TDBDatabase(Source);
    for I := 0 to self.Count-1 do
    begin
      TT:=DB.GetTableByName(self[i].Name);
      if TT<>nil then
        self[i].Compare(TT) else
        self[i].ModifyFlag:=mfDelete;
    end;
    for i:=0 to db.Count-1 do
    begin
      TT:=self.GetTableByName(DB[i].Name);
      if TT=nil then
      begin
        TT2:=TDBTable.Create(True);
        TT2.Assign(DB[i]);
        TT2.ModifyFlag:=mfAdd;
        self.Add(TT2);
      end;
    end;
  end;
end;

function TDBDatabase.CreateTableScript(Script: TStrings): Boolean;
var
  s: string;
  I: Integer;
  C: Integer;
begin
  Script.Clear;
  Result:=False;
  try
    Script.Add('USE '+StringSeparator+self.SchemaName+StringSeparator+';');
    for I := 0 to self.Count-1 do
    begin
      case self[i].ModifyFlag of
        mfAdd:
        begin
          s:='CREATE TABLE '+StringSeparator+self[i].Name+StringSeparator+' (';
          for C := 0 to self[i].Count-1 do
          begin
            s:=s+StringSeparator+self[i][C].Name+StringSeparator+' '+self[i][c].ColumnType+' DEFAULT '+self[i][c].ColumnDefault+' ,';
          end;
          s:=Copy(s, 1, Length(s)-2);
          s:=s+') COLLATE='''+self[i].Collation+''';';
          Script.Add(S);
        end;
        mfModify:
        begin
          for C := 0 to self[i].Count-1 do
            case self[i][c].ModifyFlag of
              mfModify:
              begin
                s:='ALTER TABLE '+StringSeparator+self[i].Name+StringSeparator+' CHANGE COLUMN '+StringSeparator+self[i][C].Name+StringSeparator+' '+StringSeparator+self[i][C].Name+StringSeparator+' '+self[i][c].ColumnType+' DEFAULT '+self[i][c].GetDefault+';';
                Script.Add(s);
              end;
              mfAdd:
              begin
                s:='ALTER TABLE '+StringSeparator+self[i].Name+StringSeparator+' ADD COLUMN '+StringSeparator+self[i][C].Name+StringSeparator+' '+self[i][c].ColumnType+' DEFAULT '+self[i][c].GetDefault+';';
                Script.Add(s);
              end;
              mfDelete:
              begin
                s:='ALTER TABLE '+StringSeparator+self[i].Name+StringSeparator+' DROP COLUMN '+StringSeparator+self[i][C].Name+StringSeparator+';';
                Script.Add(s);
              end;
            end;
        end;
        mfDelete:
        begin
          s:='DROP TABLE '+StringSeparator+self[i].Name+StringSeparator+';';
          Script.Add(s);
        end;
      end;
    end;
    Result:=True;
  finally

  end;
end;

function TDBDatabase.ExecSQLLine(Con: TZConnection; Line: string): Boolean;
var
  q: TZQuery;
begin
  Result:=False;
  q:=TZQuery.Create(nil);
  try
    q.Connection:=Con;
    q.SQL.Text:=Line;
    q.ExecSQL;
    Result:=True;
  finally
    q.Free;
  end;
end;

procedure TDBDatabase.GetColumns(Con: TZConnection; Schema: string; TT: TDBTable);
var
  q: TZQuery;
  TC: TDBColumn;
begin
  q:=TZQuery.Create(nil);
  try
    q.Connection:=Con;
    q.SQL.Text:='Select * from COLUMNS where TABLE_SCHEMA=:schema and TABLE_NAME=:tablename order by ORDINAL_POSITION';
    q.Params.ParamValues['schema']:=Schema;
    q.Params.ParamValues['tablename']:=TT.Name;
    q.Active:=True;
    while not q.Eof do
    begin
      TC:=TDBColumn.Create;
      TT.Add(TC);
      TC.Name:=q.FieldByName('COLUMN_NAME').AsString;
      TC.ColumnType:=q.FieldByName('COLUMN_TYPE').AsString;
      TC.FColumnDefault:=q.FieldByName('COLUMN_DEFAULT').AsString;
      q.Next;
    end;
    q.Active:=False;
  finally
    q.Free;
  end;
end;

function TDBDatabase.GetSchema(Con: TZConnection; Schema: string): Boolean;
var
  q: TZQuery;
begin
  Result:=False;
  q:=TZQuery.Create(nil);
  try
    q.Connection:=Con;
    q.SQL.Text:='Select * from SCHEMATA where SCHEMA_NAME=:schema';
    q.Params.ParamValues['schema']:=Schema;
    q.Active:=True;
    if q.RecordCount>0 then
    begin
      self.FSchemaName:=q.FieldByName('SCHEMA_NAME').AsString;
      self.FCollation:=q.FieldByName('DEFAULT_COLLATION_NAME').AsString;
      Result:=True;
    end;
    q.Active:=False;
  finally
    q.Free;
  end;
end;

function TDBDatabase.GetTableByName(value: string): TDBTable;
var
  I: Integer;
begin
  Result:=nil;
  for I := 0 to Self.Count-1 do
    if self[i].Name.ToLower=value.ToLower then
    begin
      Result:=self[i];
      Break;
    end;
end;

procedure TDBDatabase.GetTables(Con: TZConnection; Schema: string);
var
  q: TZQuery;
  TT: TDBTable;
  TC: TDBColumn;
begin
  q:=TZQuery.Create(nil);
  try
    q.Connection:=Con;
    q.SQL.Text:='Select * from TABLES where TABLE_SCHEMA=:schema order by TABLE_NAME';
    q.Params.ParamValues['schema']:=Schema;
    q.Active:=True;
    while not q.Eof do
    begin
      TT:=TDBTable.Create(True);
      self.Add(TT);
      TT.Name:=q.FieldByName('TABLE_NAME').AsString;
      TT.Collation:=q.FieldByName('TABLE_COLLATION').AsString;
      GetColumns(Con, Schema, TT);
      q.Next;
    end;
    q.Active:=False;
  finally
    q.Free;
  end;
end;

function TDBDatabase.IsModified: Boolean;
var
  I: Integer;
begin
  Result:=False;
  for I := 0 to self.Count-1 do
    if self[i].ModifyFlag<>mfNoChanges then
    begin
      Result:=True;
      Break;
    end;
end;

procedure TDBDatabase.LoadFromFile(Filename: string);
var
  st: TFileStream;
  Version: Integer;
  I, C: Integer;
  TT: TDBTable;
begin
  if Filename<>'then FFilename:=Filename;
  if FileExists(FFilename) then
  begin
    Self.Clear;
    self.FSchemaName:='';
    self.FCollation:='';
    st:=TFileStream.Create(FFilename, fmOpenRead);
    if st<>nil then
    try
      Version:=ReadIntFromStream(st);
      self.FSchemaName:=ReadStrFromStream(st);
      self.FCollation:=ReadStrFromStream(st);
      C:=ReadIntFromStream(st);
      for I := 0 to C-1 do
      begin
        TT:=TDBTable.Create(True);
        TT.ReadFromStream(st, Version);
        self.Add(TT);
      end;
    finally
      st.Free;
    end;
  end;
end;

procedure TDBDatabase.SaveToFile(Filename: string);
var
  st: TFileStream;
  I: Integer;
begin
  if Filename<>'then FFilename:=Filename;
  if DirectoryExists(ExtractFilePath(FFilename)) then
  begin
    st:=TFileStream.Create(FFilename, fmCreate);
    if st<>nil then
    try
      WriteIntToStream(st, FileVersion);
      WriteStrToStream(st, self.FSchemaName);
      WriteStrToStream(st, self.FCollation);
      WriteIntToStream(st, self.Count);
      for I := 0 to self.Count-1 do
        self[i].WriteToStream(st);
    finally
      st.Free;
    end;
  end;
end;

procedure TDBDatabase.SetCollation(const Value: string);
begin
  FCollation := Value;
end;

procedure TDBDatabase.SetSchemaName(const Value: string);
begin
  FSchemaName := Value;
end;

end.
Aufruf:

Delphi-Quellcode:
var
  DB, DB2: TDBDatabase;
  sl: TStringList;
  I: Integer;
  q: TZQuery;
begin
  DB:=TDBDatabase.Create(True);
  DB2:=TDBDatabase.Create(True);
  sl:=TStringList.Create;
  try
    if ExtactDBResource then
    begin
      DB.BuildFromDB(con1, '123456', 'testschema'); //Hier muss das Root-Passwort der DB verwendet werden.
      DB2.LoadFromFile(ExtractFilePath(Application.ExeName)+'testschema.shdat');
      DB.Compare(DB2);
      if DB.IsModified then
      begin
        if DB.CreateTableScript(sl) then
        begin
          if sl.Count>1 then
          begin
            for I := 1 to sl.Count-1 do
            begin
              DB.ExecSQLLine(con1, sl[i]);
            end;
          end;
        end;
      end;
    end;
  finally
    DB.Free;
    DB2.Free;
    sl.Free;
  end;
Die Proceduren LoadFromFile, SaveToFile, ReadFromStream und WriteToStream muss man zum Testen rausschmeißen.
Wer die dafür notwendige Unit haben möchte, kann mich ja anschreiben.
Und wer nicht ZEOS verwendet, muss das für seine vewendete DB-Komponenten anpassen.

Das ganze ist natürlich recht einfach gehalten, weil es mir ja nur um die o.g. Tabellen- und Feldeigenschaften geht. Wer mehr will, kann das ja entsprechend anpassen.
Angehängte Dateien
Dateityp: pas uTestTables2.pas (15,4 KB, 3x aufgerufen)
Gruß Hobbycoder
Alle sagten: "Das geht nicht.". Dann kam einer, der wusste das nicht, und hat's einfach gemacht.
  Mit Zitat antworten Zitat
Benutzerbild von TigerLilly
TigerLilly

Registriert seit: 24. Mai 2017
Ort: Wien, Österreich
1.237 Beiträge
 
Delphi 12 Athens
 
#10

AW: Aktualisieren der DB beim Kunden

  Alt 11. Feb 2025, 18:28
Wow. Das ist ja eine Kanone für Spatzen. Ich weiß nicht. Änderungen an der Db ziehen oft Änderungen an den Daten nach sich. Manchmal werden Felder umbenannt + der Inhalt darf aber nicht verloren gehen. Ebenso wenn sich der Typ eienes Feldes ändert. Manche Änderungen können nur gemcht werden, wenn Constraints/Indeces/Trigger uä deaktiviert und danach aktiviert werden. Das zu Automatisieren scheint mir schwierig zu sein.

Wie gesagt, ich bin mit sowas völlig zufrieden, weil ich ja selber weiß, was sich ändern muss.

Code:
 Result := Result and DoVersion(nVersion, 16, 'ALTER TABLE TBLMATERIAL DROP COLUMN BIO; ALTER TABLE TBLMATERIAL ADD BIO T_CHAR NULL',
      'Spalte Bio in der Tabelle Material erstellen ...');
    Result := Result and DoVersion(nVersion, 17, 'ALTER TABLE TBLMATERIAL ALTER COLUMN QUIDTEXT T_LONGTEXT',
      'Die Grösse der Spalte Quidtext in der Tabelle Material wird erhöht ...');
    Result := Result and DoVersion(nVersion, 18, 'ALTER TABLE TBLMATERIAL ADD LOSGROESSE T_INTEGER DEFAULT 1',
      'Bei den Materialien kann eine Losgröße hinterlegt werden ...');
    Result := Result and DoVersion(nVersion, 19, 'UPDATE TBLMATERIAL SET LOSGROESSE= 1', 'Losgröße auf 1 setzen ...');
    Result := Result and DoVersion(nVersion, 20, 'ALTER TABLE TBLMATERIAL ADD BIOZUTATEN T_TEXT;', 'Feld für Biozutaten ergänzen ...');
    Result := Result and DoVersion(nVersion, 21, 'ALTER TABLE TBLSPEISEPLAN ADD MENU T_CODE;', 'Feld für Menü ergänzen ...');
    Result := Result and DoVersion(nVersion, 22, 'ALTER TABLE TBLMATERIAL ADD ZL_TAGS T_TEXT;' + 'ALTER TABLE TBLZUTAT ADD ZL_PRO_BT T_BOOLEAN;',
      'Materialstamm TAGS ergänzen ...');
    s     := GetSQLResult('SELECT name FROM sysobjects with (nolock) WHERE (name LIKE ''%LOS%'')');
    if (Trim(s) <> '') then begin
      Result := Result and DoVersion(nVersion, 23, 'ALTER TABLE TBLMATERIAL DROP CONSTRAINT ' + s,
        'Andert die Spalte Lossgröße in der Materialien ...');
      Result := Result and DoVersion(nVersion, 23, 'ALTER TABLE TBLMATERIAL ALTER COLUMN LOSGROESSE T_FLOAT',
        'Andert die Spalte Lossgröße in der Materialien ...');
    end;
Certified Delphi Developer (2025)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 12:19 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz