Kopieren von Daten von einer DB in eine andere DB

Themen rund um den praktischen Einsatz von Firebird. Fragen zu SQL, Performance, Datenbankstrukturen, etc.

Moderator: thorben.braun

Hamburgo
Beiträge: 87
Registriert: Di 28. Mai 2019, 17:28

Hallo Volker,

noch ein Letztes:

Es war mir bisher auch nur über IB-Expert möglich, Deine SP in die DB zu bringen.

Per SQL gelang mir das nicht.

Ich vermute mal, wegen der vielen Hochkommas, die ich wohl nicht korrekt
maskiert bekomme.

Ansonsten, seitdem ich endlich kapiert habe, wo ich welche Informationen
aus den FB-System-Tabellen auslesen kann und wie der FB-Server auf bestimmte
Aktionen reagiert, habe ich mir ein eigenes Admin-Tool gebastelt, dass mir viele
Dinge einfach automatisch abnimmt, was ein Third-Party-Tool kaum leisten kann.

Eines von mehreren Beispielen:

Wenn ich in eine Tabelle, mit Bestandsdaten, eine neue Spalte anlege, die einen
Default-Wert haben soll, legt mein Tool die Spalte immer erst als "NOT NULL" an,
kopiert danach die Spalte einmal auf sich selbst und löscht danach das "NOT NULL"-Flag.

Vorteil: Der FireBird-Server setzt in den Bestands-Datensätzen automatisch
den Default-Wert in die Spalte, ohne weiteren Aufwand.


Gruss
Hamburgo
Hamburgo
Beiträge: 87
Registriert: Di 28. Mai 2019, 17:28

Hallo Volker,

so, nun habe ich das mal mit den DB-Aliases versucht.

1. Ergebnis unter FB 2.5:

a. Ich bekomme keine Fehler-Meldung mehr. :)

b. Es werden aber leider keine Daten (hier 1 Daten-Satz) kopiert. :(

Was genau habe ich gemacht:

a. In die aliases.conf habe ich eingetragen:
labor = E:\DataBase\Labor.FDB
labor_update = E:\DataBase\Labor_Update.FDB
b. Mit IB-Expert sowohl für die Quell-DB "Labor.FDB" als auch für die Ziel-DB "Labor_Update.FDB" für den Benutzer SYSDBA das Passwort via Bentzer-Manager gesetzt.

c. Das SQL-Statement gemäß Deiner Vorgabe angepasst:

Code: Alles auswählen

select * from get_xdb_insertsql('labor', 'select * from system', 17, 'system')
d. Mich per PHP via PDO-Treiber mit folgendem Connect-String verbunden:
Labor_Update, SYSDBA, HomePassword
e. das SQL-Statement, wie unter c. gezeigt, commited.

Wie auch bei den Test's zuvor habe ich zur Kontrolle die System-Tabellen
auf die Existens Deiner SP abgefragt und auch diesmal wurde sie gefunden.

Zusätzlich habe ich den betreffenden Daten-Satz aus der Quell-DB gelesen
und auch der wurde per DB-Alias gefunden.

Das ist für mich die Bestätigung, dass die DB-Aliases funktionieren.

Was kann ich nun noch tun ?
vr2
Beiträge: 141
Registriert: Fr 13. Apr 2018, 00:13

Hallo Hamburgo,

lass mal bitte die php-Ebene aus dem Spiel, das bringt bei der Analyse, ob etwas fehlt/falsch ist, nur unnötig Komplexität ins Spiel. Das Kopieren funktioniert bereits rein auf DB-Ebene.

- verwende die Aliase in IBExpert mal zur Definition der Connections, und connecte dann in IBE, dann siehst Du, ob sie funktionieren
- Du hast in IBE, wenn Du zu einer DB connectet bist, auch eine Liste mit Stored Procedures, da ist dann die SP gelistet, Du brauchst die nicht in Systemtabellen abzufragen, das macht IBE schon.
- Das select * from get_xdb_insertsql('labor', 'select * from system', 17, 'system') erzeugt einen execute block, es kopiert selber noch keine Daten.
- Diesen execute block führe mal in IBE in einem SQL-Editor aus. Dann wird kopiert

Wenn das alles passt, kannst Du php ins Spiel bringen.

Grüße, Volker
Hamburgo
Beiträge: 87
Registriert: Di 28. Mai 2019, 17:28

Hallo Volker,

so, ich habe das mal so gemacht, wie es vorgeschlagen hast.

Diesen Execute-Block habe ich erhalten:

Code: Alles auswählen

execute block RETURNS (result integer, SQL varchar(30000))
as
declare declares varchar(10000);
declare params varchar(3000);
declare flds varchar(6000);
declare wildcards varchar(3000);
declare insertsql varchar(20000);
declare dstfldcount integer;
declare crlf varchar(2);
begin
  -- daten aus srcSQL nach dsttabname laden
  crlf = ascii_char(13) || ascii_char(10);

  -- anzahl der zieltabellenfelder ermitteln
  dstfldcount = (select count(rdb$field_position)
                 from rdb$relation_fields
                 where trim(rdb$relation_name) = 'SYSTEM');

  -- struktur der zieltabelle ermitteln
  with
  a (fldname, fldpos) as (
    select trim(rdb$field_name) fldname, rdb$field_position
    from rdb$relation_fields
    where rdb$system_flag = 0 and trim(rdb$relation_name) = 'SYSTEM'
    order by 2
    rows 17),

  b (declares, flds, wildcards, params) as (
    select :crlf || list('declare ' || fldname || ' TYPE OF COLUMN SYSTEM.' || fldname || ';', :crlf) || :crlf,
           list(fldname), list('?'), list(':' || fldname)
    from a)

  select * from b
  into :declares, :flds, :wildcards, :params;

  -- kurzes statement ohne feldliste?
  if (:dstfldcount <= 17) then
    insertsql = '''insert into SYSTEM values (' || :wildcards || ')''';
  else
    insertsql = '''insert into SYSTEM (' || :flds || ')  values (' || :wildcards || ')''';

  sql = 'execute block returns (result integer) as ' || :declares || 'begin ';
  sql = sql || :crlf || '  result = 0;';
  sql = sql || :crlf || '  for execute statement ''select * from system'' ';
  sql = sql || :crlf || '  on external ''labor'' ';
  sql = sql || :crlf || '  into ' || :params || ' ' || :crlf || '  do ' || :crlf || '  begin ';

  -- eintragen lokal
  sql = sql || :crlf || '    execute statement (' || :insertsql || ') (' || :params || ');';
  sql = sql || :crlf || '    result = result + 1; ' || :crlf || '  end ' || :crlf || '  suspend; ' || :crlf || 'end;';

  in autonomous transaction
  do
    execute statement sql
    -- anzahl der inserts zurückgeben
    into :result;
  suspend;
end
Leider habe ich wieder einen Fehler erhalten. Warum ist mir ein Rätzel.

Der String, über den er meckert steht in der Spalte ""SCRIPT_USER_NR"
und ist eigentlich viel zu kurz, um einen Overflow zu produzieren.

Auch braucht er eigentlich keine Data-Conversersion, da Source und Ziel-
Tabelle absolut gleich sind.

Vielleicht findest Du ja den Grund in den 3 Bildern.

Die Tabelle hat nur 1 Datensatz.

Danke und Gruss
Dateianhänge
Labor_Update03.jpg
Labor_Update03.jpg (138.31 KiB) 815 mal betrachtet
Labor_Update02.jpg
Labor_Update02.jpg (152.87 KiB) 815 mal betrachtet
Labor_Update01x.jpg
Labor_Update01x.jpg (140.97 KiB) 815 mal betrachtet
bfuerchau
Beiträge: 349
Registriert: Mo 7. Mai 2018, 18:09

Prüfe mal das SQL im Ergebnis ohne die Ausführung.
M.a.W. lass mal den " in autonomous transaction ..." weg.

Es kann durchaus sein, dass ein Syntaxfehler vorliegt und die Meldung ein Folgefehler ist.
vr2
Beiträge: 141
Registriert: Fr 13. Apr 2018, 00:13

Hallo Hamburgo,

Dein zweiter Screenshot zeigt die Ursache: Du hast unterschiedliche Feldreihenfolgen zwischen Quell- und Zieltabelle. Die SP nimmt sich die Reihenfolge aus der Zieltabelle. Feld SCRIPT_USER_NR wird versucht nach BATCH_DATE zu übertragen, und diese Konvertierung scheitert natürlich.

Du kann ja unterschiedliche Feldreihenfolgen in Quell- und Zieltabelle haben, wenn es dafür einen guten Grund gibt, aber dann musst Du die Quelltabelle so abfragen, dass die Reihenfolge der Felder im Quellselect zu der Reihenfolge der Felder der Zieltabelle passt. Andernfalls mach einfach bei beiden Tabellen die gleiche Feldreihenfolge. Und frag die dann auch so ab ;-)

Grüße, Volker
bfuerchau
Beiträge: 349
Registriert: Mo 7. Mai 2018, 18:09

Das ist auch der Grund, warum ich sowas im Programm und nicht in der eingeschränkten SQL-Sprache mache.
Dabei kann man dann auch ebenso erforderliche Konvertierungen (casts) mit unterbringen oder bei fehlenden Mappings Felder auslassen oder Defaultwerte einbringen.
Das Ergebnis dieses Codes lässt sich dann durchaus auch als Execute Block generieren, aber auch hier ist die Ausführung im Client, wenn man auf dem selben Rechner ist, durch Parallelisierung (Reader-Thread, Writer-Thread) durchaus mit ansehnlichem Durchsatz zu leisten (je nach Zeilenlänge mehr als 4000 Inserts/Sekunde).
vr2
Beiträge: 141
Registriert: Fr 13. Apr 2018, 00:13

Es hat nichts mit der Sprache zu tun, wenn eine Prozedur nicht richtig benutzt wird, und casts, mapping und defaults sind in SQL auch kein Problem. Die SP erhebt nicht den Anspruch, alle Fehlersituationen abzudecken, sondern erzeugt SQL, um Daten serverseitig von einer DB zu einer anderen zu kopieren, geht dabei von einem einfachen Zusammenhang aus (übereinstimmende Feldreihenfolge zwischen Quelle und Ziel, kompatible Typen), nicht mehr, nicht weniger.

Hamburgo, die Antwort auf Deine Frage steht zwei Postings weiter oben.

Grüße, Volker
Hamburgo
Beiträge: 87
Registriert: Di 28. Mai 2019, 17:28

Hallo Volker,

danke für Deine Erläuterung.

Ich war davon ausgegangen, dass die Feld-Reihenfolge keine Rolle spielt,
sondern das nur gewährleistet sein muss, dass alle Felder in Quell- und
auch der Ziel-DB vorhanden sein müssen.

Aber gut, ist halt so und ja auch keine grosse Hürde das anzupassen.

Bin gespannt, ob es das tatsächlich ist. Wäre cool.

Danke und Gruss
Hamburgo
Beiträge: 87
Registriert: Di 28. Mai 2019, 17:28

Hallo Volker,

vor lauter Ehrgeiz Dein Script/SP zum Laufen zu kriegen und einfach zu liefern, was es dazu
braucht, habe ich den eigentlichen Verwendungszweck der SP völlig aus den Augen verloren.

Sinn und Zweck des Ganzen ist ja im Rahmen eines Software-UPDATEs die Kunden-DB mit
einer neuen Version des DB-Schemas zu versehen.

Das hatte ich auch im Laufe der ersten Posts zu diesem Thread so beschrieben.

Ergo ist das Schema der neuen Update-DB logischerweise immer anders, als das der
aktuellen Kunden-DB.

Wenn die Logik Deiner SP es jedoch zwingend erfordert, dass die Anzahl UND auch die
Reihenfolge der Felder pro Tabelle in beiden DBs gleich sein muss, dann ist sie für diesen
Zweck leider nicht geeignet.
Da liegt wohl leider ein Missverständnis vor.

Ich bräuchte schon eine Logik, die in der Lage ist z.B. in der Tabelle MASCHIENEN die Daten des Feldes SERIEN_NR von der alten DB in die neue DB zu übertragen, auch wenn das Feld im neuen Schema eine neue Position hat, weil davor entweder neue Felder hinzu gekommen sind oder evtl. auch welche gelöscht wurden.

Auch müsste die SP merken, dass ein Feld einer Tabelle im neuen Schema evtl. nicht mehr da ist und dann halt einfach Alt-Daten verloren gehen, weil nicht kopiert werden kann.

Ist das ohne größeren Aufwand machbar ?

Danke und viele Grüße
Antworten