Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   violation of FOREIGN KEY constraint (https://www.delphipraxis.net/191174-violation-foreign-key-constraint.html)

Zwirbel 16. Dez 2016 08:03

Datenbank: FB • Version: 3 • Zugriff über: Delphi Seattle

violation of FOREIGN KEY constraint
 
Hallo,

um Daten aus unserer alten "Datenbank" (Paradox) nach FB3 zu heben habe ich eine kleine Delphi Applikation geschrieben. Dabei stellte ich fest, dass ich in einigen Detailtabellen Datensätze enthalten habe, wozu gar keine entsprechende Datensätze in der dazugehörigen Mastertabelle vorhanden sind. Also Karteileichen, wenn man so will. Das führt dann aber dazu, dass ich bei der Migration Fehler erhalte.

Delphi-Quellcode:
FireDac Fehler
The application performed an incorrect operation with the database.
-------------------------------------------
Message text: violation of FOREIGN KEY constraint "FK_TA_BagStatCal" on table "TA_BagStatCal"
Foreign key reference target does not exist
Problematic key value is ("BagStatListId" = 0)
Error code: 335544466
Error kind: FKViolated
Server object:
Command text offset:
-------------------------------------------
Command text: INSERT INTO "TA_BagStatCal" ("BagStatCalId", "BagStatCalListId", "ReferenceDate", "BagStatListId", "DayToUse", "Memo", "Created", "Modified", "CreatedBy", "ModifiedBy", "UserId", "State", "StatusId") VALUES (:BagStatCalId, :BagStatCalListId, :ReferenceDate, :BagStatListId, :DayToUse, :Memo, :Created, :Modified, :CreatedBy, :ModifiedBy, :UserId, :State, :StatusId)

Command parameters:
  BAGSTATCALID=8767
  BAGSTATCALLISTID=2
  REFERENCEDATE=01.01.2007
  BAGSTATLISTID=0
  DAYTOUSE=1
  MEMO=
  CREATED=22.07.2014 10:40:15
  MODIFIED=22.07.2014 10:40:15
  CREATEDBY=1
  MODIFIEDBY=1
  USERID=1
  STATE=22.07.2014 10:40:15
  STATUSID=0
  BagStatCalId=<null>
  BagStatCalListId=<null>
  ReferenceDate=<null>
  BagStatListId=<null>
  DayToUse=<null>
  Memo=<null>
  Created=<null>
  Modified=<null>
  CreatedBy=<null>
  ModifiedBy=<null>
  UserId=<null>
  State=<null>
  StatusId=<null>
-------------------------------------------
Exception class name: EIBNativeException
FireDAC error code: 1400
FireDAC object name: frmAMU.qry_FB
Meine Lösung sieht nun derzeit so aus, vor der Migration alle Quelldaten die in einer Master/Detailbeziehung stehen aufzubereiten und Detaildatensätze ohne passenden Masterdatensatz in den Originaldaten löschen. Aber das dauert alles extrem lange. Bei den meisten Kunden würde das keine große Rolle spielen, ob der einmalige Migrationsvorgang nun 10 Minuten oder 1 Stunde dauert. Aber in einigen Situationen sind 24/7-Systeme zu migrieren, da kommt es auf jede Minute an.

Delphi-Quellcode:
procedure TfrmAMU.CleanupTable_BagStatCal(const aTA_BagStatListAvailable : Boolean);
  { Die Detailtabelle BagStatCal kann Datensätze enthalten die keine Referenz zur
    Mastertabelle BagStatList haben. Diese verweisten Datensätze sind zu
    löschen. »aTA_BagStatListAvailable« gibt an, ob die Tabelle "BagStatList"
    auch existiert. Wenn sie nicht existiert, dann werden die Daten in "BagStatCal"
    alle gelöscht. }
var
  StartTime,
  StopTime        : TDateTime;
  Statement       : string;
  Id, i           : Integer;
  DoDelete        : Boolean;
begin
  StartLog(StartTime, 'CleanupTable_BagStatCal');
  try
    try
      { Liste alle BagStatListId-Datensätze in der Paradoxtabelle
        "BagStatCal" auf die es in der Detailtabelle gibt. }
      Statement := 'SELECT BagStatListId FROM BagStatCal GROUP BY BagStatListId';
      DoLog(Statement);
      qry_Pdx_Repair_Select.SQL.Clear;
      qry_Pdx_Repair_Select.SQL.Add(Statement);
      qry_Pdx_Repair_Select.Open;
      qry_Pdx_Repair_Select.First;
      for i := 0 to Pred(qry_Pdx_Repair_Select.RecordCount) do begin
        if aTA_BagStatListAvailable then begin
          { Schauen ob es zu jedem BagStatListId-Datensatz in der Mastertabelle
            auch Datensätze gibt. }
          Id := qry_Pdx_Repair_Select.FieldByName('BagStatListId').AsInteger;
          Statement := Format('SELECT * FROM BagStatList WHERE BagStatListId = %d', [Id]);
//          DoLog(Statement);
          qry_Pdx_Repair_Delete.SQL.Clear;
          qry_Pdx_Repair_Delete.SQL.Add(Statement);
          qry_Pdx_Repair_Delete.Open;
          DoDelete := qry_Pdx_Repair_Delete.RecordCount = 0;
          qry_Pdx_Repair_Delete.Close;
        end else begin
          Id := qry_Pdx_Repair_Select.FieldByName('BagStatListId').AsInteger;
          DoDelete := True;
        end;

        if (i mod 5) = 0 then
          MWApplicationProcessMessages;

        if FStoped then
          Break;

        { Es hat Leichen. Diese Detaildatensätze löschen. }
        if DoDelete then begin
          Statement := Format('DELETE FROM BagStatCal WHERE BagStatListId = %d', [Id]);
          DoLog(Format('Delete orphans. %s', [Statement]));
          qry_Pdx_Repair_Delete.SQL.Clear;
          qry_Pdx_Repair_Delete.SQL.Add(Statement);
          qry_Pdx_Repair_Delete.ExecSQL;
        end;
        qry_Pdx_Repair_Select.Next;
      end;
    finally
      qry_Pdx_Repair_Select.Close;
    end;
    StopLog(StartTime, StopTime, 'CleanupTable_BagStatCal');
  except
    on E : Exception do begin
      HandleExceptionPrim(E, E.message, 1001, 'CleanupTable_BagStatCal');
      raise;
    end;
  end;
end;
Es ist sicherlich zu bedenken, dass mit ausgetüftelten SQL-Statements auf den Original-Paradox-Daten kein Blumentopf zu gewinnen ist.

Die beiden Tabellen die zum Fehler oben passen sehen so aus:

Delphi-Quellcode:
/* Mastertabelle +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
CREATE TABLE "TA_BagStatList" (
  "BagStatListId"             "DO_UniqueId",
  "ShortDesc"                 "DO_ShortDesc" NOT NULL,
  "LongDesc"                  "DO_LongDesc",
  "OPS_DayBased"              SMALLINT,
  "NrOfDays"                  SMALLINT,
  "Memo"                      "DO_Memo",
  "Created"                   "DO_Created",
  "Modified"                  "DO_Modified",
  "CreatedBy"                 "DO_CreatedBy",
  "ModifiedBy"                "DO_ModifiedBy",
  "UserId"                    "DO_UserId",
  "State"                     "DO_State",
  "StatusId"                  "DO_StatusId"
);

ALTER TABLE "TA_BagStatList" ADD CONSTRAINT "PK_BagStatList" PRIMARY KEY ("BagStatListId");
CREATE SEQUENCE "SE_TA_BagStatList_BagStatListId" START WITH 0 INCREMENT BY 1; ALTER SEQUENCE "SE_TA_BagStatList_BagStatListId" RESTART WITH 0;

/* Detailtabelle +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
CREATE TABLE "TA_BagStatCal" (
  "BagStatCalId"              "DO_UniqueId",
  "BagStatCalListId"          "DO_UniqueId",
  "ReferenceDate"             "DO_TimeStamp" NOT NULL,
  "BagStatListId"             "DO_UniqueId",
  "DayToUse"                  SMALLINT,
  "Memo"                      "DO_Memo",
  "Created"                   "DO_Created",
  "Modified"                  "DO_Modified",
  "CreatedBy"                 "DO_CreatedBy",
  "ModifiedBy"                "DO_ModifiedBy",
  "UserId"                    "DO_UserId",
  "State"                     "DO_State",
  "StatusId"                  "DO_StatusId"
);

ALTER TABLE "TA_BagStatCal" ADD CONSTRAINT "PK_BagStatCal" PRIMARY KEY ("BagStatCalId");
CREATE INDEX "IF_TA_BagStatCal_ReferenceDate" ON "TA_BagStatCal" "BagStatCalListId","ReferenceDate";
ALTER TABLE "TA_BagStatCal"
  ADD CONSTRAINT "FK_TA_BagStatCal"
  FOREIGN KEY ("BagStatListId")
  REFERENCES "TA_BagStatList"("BagStatListId")
  ON DELETE CASCADE; /* löscht alle Datensätze in der Detailtabelle "TA_BagStatCal", wenn der Masterdatensatz in "TA_BagStatList" gelöscht wird */

CREATE SEQUENCE "SE_TA_BagStatCal_BagStatCalId" START WITH 0 INCREMENT BY 1; ALTER SEQUENCE "SE_TA_BagStatCal_BagStatCalId" RESTART WITH 0;

/* Trigger für eine eindeutige Id */
SET TERM ^;
CREATE OR ALTER TRIGGER "TR_TA_BagStatCal_BI" FOR "TA_BagStatCal" ACTIVE BEFORE INSERT
AS BEGIN
  IF(NEW."BagStatCalId" IS NULL) THEN NEW."BagStatCalId" = GEN_ID("SE_TA_BagStatCal_BagStatCalId",1);
END ^

Hat jemand eine Idee wie man das lösen kann, ohne die Originaldaten zu bereinigen? Also quasi beim Einfügen der Daten dafür zu sorgen, dass ein Detaildatensatz nicht eingefügt wird. Derzeit füge ich die Daten während der Migration per INSERT ein:

Delphi-Quellcode:
            InsertSQL := Format('INSERT INTO "%s" (%s) VALUES (%s)', [TableName, InsertFieldNames, InsertFieldValues]);
            qry_FB.SQL.Add(InsertSQL);
...
                { Nun die Werte je nach Datentyp übertragen von der Quelle ins Ziel }
                case tbl_Pdx.Fields[k].DataType of
                  ftString          :
                    qry_FB.ParamByName(FieldNameUpper).AsString := tbl_Pdx.FieldByName(FieldNameMixed).AsString;
                  ftSmallint,
                  ftInteger,
                  ftWord,
                  ftAutoInc         :
                    qry_FB.ParamByName(FieldNameUpper).AsInteger := tbl_Pdx.FieldByName(FieldNameMixed).AsInteger;
...
                qry_FB.ExecSQL(InsertSQL);

Vielen Dank im Voraus. Gruß, Markus

DeddyH 16. Dez 2016 08:08

AW: violation of FOREIGN KEY constraint
 
Kannst Du nicht ganz einfach den FK zunächst weglassen, die Daten komplett einlesen, anschließend in der neuen DB alle Detaildatensätze ohne entsprechenden Master löschen und danach erst den FK definieren?

Jumpy 16. Dez 2016 08:09

AW: violation of FOREIGN KEY constraint
 
Keine Ahnung ob das mit Firebird geht, aber ich würde einfach die FK-Constrains (vorübergehend) deaktivieren, alle Daten in die Firebird-DB schießen und diese dann dort mit entsprechenden SQL-Statements schnell(?) bereinigen. Anschließend, wenn alles sauber ist, die Constraints wieder aktivieren.

Edit: 2 Dumme ein Gedanke, aber keine rote Box :).

Jasocul 16. Dez 2016 08:14

AW: violation of FOREIGN KEY constraint
 
Moin,
wenn ich das richtig sehe, überträgst du jeden Datensatz einzeln.
Was spricht dagegen, vor der Übertragung zu prüfen, ob in der Mastertabelle überhaupt ein passender Datensatz existiert?
Hat die Mastertabelle keinen passenden Key, überträgst du den Datensatz einfach nicht.

Zwirbel 16. Dez 2016 08:16

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von Jumpy (Beitrag 1356301)
Keine Ahnung ob das mit Firebird geht, aber ich würde einfach die FK-Constrains (vorübergehend) deaktivieren, alle Daten in die Firebird-DB schießen und diese dann dort mit entsprechenden SQL-Statements schnell(?) bereinigen. Anschließend, wenn alles sauber ist, die Constraints wieder aktivieren.

Dazu muss ich mir anschauen, wie ich das regel, bestimmte contrains erst hinterher einzuschalten, derzeit habe ich es so geregelt, dass das Migrationsprogramm die gesamte Datenbank per SQL-Skript über IBExpert erzeugt. Dann muss ich den Part wohl splitten, erst mal nur die Tabellen anlegen, Daten reinpumpen und hinterher die Contraints anlegen, hat vllt. allgemein den Vorteil, dass das schneller geht.

Die spannendere Frage ist, wie würde denn ein solches SQL-Statement zum Bereinigen aussehen? Da ich wie gesagt von SQL noch nicht arg viel verstehe, kann mir da jemand möglichst etwas beispielhaftes zeigen? Danke, Gruß, Markus

Zwirbel 16. Dez 2016 08:23

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von Jasocul (Beitrag 1356304)
Moin,
wenn ich das richtig sehe, überträgst du jeden Datensatz einzeln.
Was spricht dagegen, vor der Übertragung zu prüfen, ob in der Mastertabelle überhaupt ein passender Datensatz existiert?
Hat die Mastertabelle keinen passenden Key, überträgst du den Datensatz einfach nicht.

Der Gedanke war mir während des Schreibens meiner Frage auch gekommen. Aber diese Einfüge-Routine weiß von Master/Detail nichts, weil sie global für alle Tabellen gleich arbeitet. Da müsste ich dann spezielle Anweisungen schreiben, bei bestimmten Tabellennamen darauf zu achten, ob in einer zweiten (Master-)Tabelle ein passender Datensatz enthalten ist. Ist auf jeden Fall eine Option über die ich vllt. doch noch mal weiter grübeln sollte. Ist vllt. pragmatischer. Danke.

Assarbad 16. Dez 2016 08:31

AW: violation of FOREIGN KEY constraint
 
Mal ne andere Idee. Warum nicht eine Pivottabelle erzeugen welche nur jene IDs (BagStatListId und BagStatCalId) listet, die auch eine Entsprechung haben. Alternativ könnte auch eine Pivottabelle reichen bei der halt die ID aus der Mastertabelle auf NULL (ist also wichtig daß das geht) steht wenn kein entsprechender Datensatz existiert und dann beim späteren Migrieren alle "WHERE BagStatListId=NULL" auszusieben.

Eine solche Pivottabelle könnte sich auch gut eignen noch gleich festzuhalten welche Datensätze bereits migriert oder eben ungültig sind (verstoßen gegen die FOREIGN KEY Auflage).

DeddyH 16. Dez 2016 08:33

AW: violation of FOREIGN KEY constraint
 
Oder einfach über einen INNER JOIN:
SQL-Code:
SELECT
  Detail.*
FROM
  Detail
  JOIN
    Master ON Master.PK = Detail.FK

Assarbad 16. Dez 2016 08:37

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von DeddyH (Beitrag 1356311)
Oder einfach über einen INNER JOIN:
SQL-Code:
SELECT
  Detail.*
FROM
  Detail
  JOIN
    Master ON Master.PK = Detail.FK

Wenn Paradox das kann. Kenne mich mit Paradox nämlich nicht aus. Aber ich verstand es so, daß die Daten erst bei Ankunft in Firebird mit klassischem SQL zugreifbar sind.

DeddyH 16. Dez 2016 08:39

AW: violation of FOREIGN KEY constraint
 
*Psst* Ganz ehrlich: ich weiß es auch nicht, habe mich mit Paradox nie wirklich beschäftigt. Aber wenn, dann wäre das wahrscheinlich die eleganteste Option ;)

Jumpy 16. Dez 2016 08:59

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von Zwirbel (Beitrag 1356305)
Die spannendere Frage ist, wie würde denn ein solches SQL-Statement zum Bereinigen aussehen? Da ich wie gesagt von SQL noch nicht arg viel verstehe, kann mir da jemand möglichst etwas beispielhaftes zeigen? Danke, Gruß, Markus

Konkret müsste man dann im Einzelfall schauen, aber so ungefähr:

SQL-Code:
--Wenn man sich erstmal die betroffenen Datensätze angucken will
Select * From Detail_Tabelle
Where FK_Feld not in
(Select PK_Feld From Master_Tabelle)

--Wenn man's dann löschen will
Delete From Detail_Tabelle
Where FK_Feld not in
(Select PK_Feld From Master_Tabelle)
Das wäre jetzt für den Fall, das z.B. im Master sowas wie Aufträge sind, während in Detail die dazugehörigen Auftragspositionen stehen und wo es jetzt Auftragspositionen gibt, zu denen es im Master gar keine Aufträge gibt.

hoika 16. Dez 2016 10:28

AW: violation of FOREIGN KEY constraint
 
Hallo,
Paradox, die gute alte Zeit ;)

Also ich würde vom SQL die Finger weglassen.
Sowas wie

Delete From DetailTable Where DetailTable.MasterId not In (Select MasterTable.MasterId From MasterTable)
dauert unter Paradox i.d.R. sehr lange.

Wenn du eh an alle Tabellen ranmusst:
Select DetailId,MasterId From DetailTable -> rein in eine TStringList DetailList
Select MasterTable.MasterId From MasterTable -> rein in eine TStringList MasterList

und jetzt alle Details durchlaufen und nach der MasterId in der MasterList suchen,
wenn nicht vorhanden, den Detailsatz löschen.

nahpets 16. Dez 2016 10:51

AW: violation of FOREIGN KEY constraint
 
Was ich hier aus dem Quelltext nicht ganz so genau entnehmen kann:
Delphi-Quellcode:
            InsertSQL := Format('INSERT INTO "%s" (%s) VALUES (%s)', [TableName, InsertFieldNames, InsertFieldValues]);
            qry_FB.SQL.Add(InsertSQL);
...
                { Nun die Werte je nach Datentyp übertragen von der Quelle ins Ziel }
                case tbl_Pdx.Fields[k].DataType of
                  ftString :
                    qry_FB.ParamByName(FieldNameUpper).AsString := tbl_Pdx.FieldByName(FieldNameMixed).AsString;
                  ftSmallint,
                  ftInteger,
                  ftWord,
                  ftAutoInc :
                    qry_FB.ParamByName(FieldNameUpper).AsInteger := tbl_Pdx.FieldByName(FieldNameMixed).AsInteger;
...
                qry_FB.ExecSQL(InsertSQL);
Werden die Daten satzweise übernommen?

Wenn ja, Methode einfach, billig und unelegant:
Delphi-Quellcode:
Try
  qry_FB.ExecSQL(InsertSQL);
except
  on e : Exception do begin
    // ggfls. Fehlertyp abfragen und Fehlermeldung in 'ne Logdatei schreiben.
  end;
end;
Wie greifts Du auf die Datenbanken zu, noch über die BDE?

Wenn ja, dann schau Dir (soweit in Deinem Delphi vorhanden) bitte einmal die Komponente TBatchMove an.

Da kann man einiges Konfigurieren:

Quelle und Ziel.
Feldmapping.
Verhalten im Fehlerfalle.
Ausgabetabelle für die Datensätze mit Schlüsselverletzungen.

Eventuell etwas Literatur zu der Komponente:

http://docwiki.embarcadero.com/RADSt...wenden_-_Index
http://edn.embarcadero.com/article/25620
http://docs.embarcadero.com/products...epart_xml.html

Ach, was mir noch einfiel:

Wird die BDE benutzt, so kann man in der Localsql.hlp nachlesen, was mit SQL möglich ist. Dazu gehören auch die "handelsüblichen" Joins.

Die Hilfedatei liegt gewöhnlich im gleichen Verzeichnis, wie BDEAdmin.exe.

Jumpy 16. Dez 2016 11:31

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von hoika (Beitrag 1356340)
Hallo,
Also ich würde vom SQL die Finger weglassen.
Sowas wie

Delete From DetailTable Where DetailTable.MasterId not In (Select MasterTable.MasterId From MasterTable)
dauert unter Paradox i.d.R. sehr lange.

Das war jetzt auch eher so gedacht, dass die Daten ohne Constraints, in die neue/modernere DB übertragen werden und da dann das SQL ausgeführt wird, bevor die Constraints dann aktiviert werden.

In Paradox/BDE würde ich sowas auch nicht machen wollen.

hoika 16. Dez 2016 13:21

AW: violation of FOREIGN KEY constraint
 
Hallo,
ah ja, das ginge.
Wobei die Geschwindigkeit dann trotzdem mau sein kann, wegen der fehlenden Indizes.

Also noch mal:
- DB ohne foreign keys (FKs) erzeugen.
- Daten rüberschaufeln.
- Datenleichen löschen
- FKs erzeugen.

In Firebird gibt es die Möglichkeit, eine Restore einer DB ohne aktivierte Indizes zu machen.
Danach muss man die aber afaik per Alter Index Active von Hand aktivieren.

nahpets 16. Dez 2016 13:38

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von hoika (Beitrag 1356375)
Wobei die Geschwindigkeit dann trotzdem mau sein kann, wegen der fehlenden Indizes.

Zum Einfügen braucht man keinen Index.

Praktische Erfahrung aus der Vergangenheit:

Daten ohne Index in die Datenbank schieben und anschließend Index erzeugen geht deutlich schneller, als Daten bei aktivem Index in die Datenbank zu schieben (vor allem bei großen Datenmengen).

Meiner Meinung nach:

Datenbank ohne Index
Datenbank ohne Constraints
Daten rein
Index erstellen
ggfls. bereinigen
Constrains erstellen

Was eventuell auch die eine oder andere Stunde sparen kann:

Aus den "alten" Daten Insertscripte als Datei(en) erstellen. Alle 1000 (oder so) Sätze ein Commit ins Script.

Scripte mit Datenbankmitteln einlesen (bei Oracle z. B. SQLLoader).

Index erstellen.
Bereinigen.
Constraints erstellen.

Wenn man Pech hat, muss man die letzten beiden Schritte wiederholen, bis alle Fehler behoben wurden.

jobo 16. Dez 2016 13:42

AW: violation of FOREIGN KEY constraint
 
Paradox könnte das können.
Der SQL Dialekt nennt sich "local SQL"
Hab auf die Schnelle das hier gefunden
http://www.ayton.id.au/gary/it/Delphi/C_sql01.htm

Irgendwo schwirren sicher auch die original HLP Files rum, die die Syntaxmöglichkeiten beschreiben.

Wenn eine gefilterte Abfrage mit konsistenten Daten (s.o.) nicht gelingt, sollte es doch sicher in der Zieldb gelingen, wie vorgeschlagen mit abgeschalteten FK Constraints.
Notfalls auch der Weg über Exception on Insert Error
(das kann natürlich auch andere Gründe haben und man muss die Fehlermeldung prüfen) bevor man die Insert Exception auf sich beruhen lässt.

hoika 17. Dez 2016 06:27

AW: violation of FOREIGN KEY constraint
 
Hallo,
mein SQL-Befehl von oben läuft ja.
Er ist halt nur langsam.

Vielleicht sollte man den beim Kunden auf einer Kopie der Datenbank mal laufen lassen.
Das Problem ist ja, daß man nie mit Echtdaten arbeiten kann.

fully populate your database

wie es so schön heißt ...

jobo 17. Dez 2016 09:58

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von hoika (Beitrag 1356421)
mein SQL-Befehl von oben läuft ja.
Er ist halt nur langsam.

Welches SQL, das "Delete where not in"?

Diese Konstrukte sind in vielen DB nicht so dolle implementiert, erfahrungsgemäß, also gefühlt (ich habe natürlich noch nie eine solche Implementierung gesehen)
Viel versprechender ist es andersrum vorzugehen mittels eines Joins wie ihn DeddyH es hier
http://www.delphipraxis.net/1356311-post8.html
vorgeschlagen hat.

Es ist auch naheliegend, weil die Zeit nicht erst für Aufräumen "verschwendet" wird, sondern gleich die richtigen Daten selektiert werden. Also quasi "die guten ins Töpfchen, die schlechten .. " interessieren gar nicht.


Mal so nebenbei:
Ist Dir bekannt, dass die BDE heterogen arbeiten kann und Du sowas machen kannst*:
Code:
 insert into :firebirdtalias:desttable (f1,f2,f3)
SELECT
  Detail.*
FROM
  :bdetalias:Detail
  JOIN
    :bdetalias:Master ON Master.PK = Detail.FK
* Es sollte so gehen, ich hab hier nichts am Start, um das auszuprobieren.
Und es sollte halbwegs flott sein. Du brauchst natürlich einen BDE Alias auf die Zieldatenbank, vielleicht per ODBC, vlt. gibts auch einen nativen Treiber (Interbase?)


Grundsätzlich bei solchen Themen:
Je größer die Daten-Popelei** bei der Migration wird, desto eher gehe ich so vor.
Alle Altdaten werden in die Zieldatenbank übertragen, dort ist man idR flexibler (SQL) und schneller als mit dem veralteten Originalsystem.
Je nach Größe/Aufgabenstellung kann man das Verfahren dann abspecken / optimieren, natürlich vor allem dann, wenn es keine einmalige Sache ist, sondern auf mehreren Kundensystemen erfolgen soll.
Eine Variante des Vorgehens ist der vollständige Import der Altdaten in einen temporären Server mit identischer DB Installation. IdR kann man dann beim finalen Import im Zielsystem sehr bequem aus der Schwesterdb abfragen, also so halb heterogen.

** Popelei betrifft oft auch den Umbau von Feldinhalten. Dann spätestens sind wir beim Thema ETL (oder ELT), das geht wunderbar mit SQL und berechneten Feldern ohne meterlangen Programmcode. Dazu kann man auch schön Views nutzen, die die Transformation liefern. Kleine Fehler, die im Rahmen von Test auftauchen, sind schnell im View korrigiert ohne Programmcode zu ändern.
Natürlich gibt es auch super ETL tools, ist aber vielleicht für eine eigene Produktmigration nicht so der Knaller.

Zwirbel 20. Dez 2016 08:56

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von nahpets (Beitrag 1356347)
Was ich hier aus dem Quelltext nicht ganz so genau entnehmen kann:
Delphi-Quellcode:
            InsertSQL := Format('INSERT INTO "%s" (%s) VALUES (%s)', [TableName, InsertFieldNames, InsertFieldValues]);
            qry_FB.SQL.Add(InsertSQL);
...
                { Nun die Werte je nach Datentyp übertragen von der Quelle ins Ziel }
                case tbl_Pdx.Fields[k].DataType of
                  ftString :
                    qry_FB.ParamByName(FieldNameUpper).AsString := tbl_Pdx.FieldByName(FieldNameMixed).AsString;
                  ftSmallint,
                  ftInteger,
                  ftWord,
                  ftAutoInc :
                    qry_FB.ParamByName(FieldNameUpper).AsInteger := tbl_Pdx.FieldByName(FieldNameMixed).AsInteger;
...
                qry_FB.ExecSQL(InsertSQL);
Werden die Daten satzweise übernommen?

Wenn ja, Methode einfach, billig und unelegant:
Delphi-Quellcode:
Try
  qry_FB.ExecSQL(InsertSQL);
except
  on e : Exception do begin
    // ggfls. Fehlertyp abfragen und Fehlermeldung in 'ne Logdatei schreiben.
  end;
end;

Ja, die Daten werden datensatzweise übertragen. Genau so (Exception abfangen) habe ich es jetzt erst mal gemacht.

Zitat:

Wie greifts Du auf die Datenbanken zu, noch über die BDE?
Ja, habe mir diese alten BDE-Komponenten für Delphi Seattle geholt. Der Versuch über ODBC zuzugreifen und damit die BDE aus der Migrations-Applikation heraus zu halten scheitert daran, dass wir ausschließlich Paradox Tabellen im 7er Format einsetzen und das geht über ODBC nicht.

Wir haben uns jetzt dafür entschieden erst mal keine weitere Zeit in eine Optimierung zu investieren. Zum Glück haben wir von fast allen Kunden Echtdaten hier und haben so zumindest schon mal einen Überblick wo es überall haken kann und notfalls schalte ich diese derzeit zeitintensiven Reparaturroutinen per Option in der Migrations-Applikation ein.

Danke an alle für die vielfältigen und hilfreichen Informationen. Gruß, Markus

jobo 20. Dez 2016 17:39

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von Zwirbel (Beitrag 1356660)
..
Ja, habe mir diese alten BDE-Komponenten für Delphi Seattle geholt. Der Versuch über ODBC zuzugreifen und damit die BDE aus der Migrations-Applikation heraus zu halten scheitert daran, dass wir ausschließlich Paradox Tabellen im 7er Format einsetzen und das geht über ODBC nicht.

..

Was wollt Ihr damit bezwecken, die BDE da raus zu halten?
Es gibt nichts besseres als nativen Zugriff. Wem nützt der None-BDE Versuch, wenn die Einsatzbereiche eh alle BDE-"verseucht" sind? Ich würde es eher umgekehrt machen und eine entsprechend alte (alt genug), native Delphi Version nehmen. Das verspricht die wenigsten Fehlerquellen.
Eine Datenkopiermaschine sollte man auch mit alten Delphis noch gut hinbekommen oder?
(Nur mal so als Gedanke)

nahpets 20. Dez 2016 18:09

AW: violation of FOREIGN KEY constraint
 
Hierbei würd' ich sogar noch weiter gehen:

Nicht nur ein Delphi, dass noch die BDE mitbringt, sondern auch ein Windows, auf dem die BDE garantiert noch problemlos läuft.

Das war irgendwo vor Windows XP mit Delphi 7?

Wenn man daraus dann die entsprechenden Scripte für die Datenmigration erstellt, kann man diese Textdateien mit den Mitteln des neuen Systemes einlesen.

Bei Datenmigrationen hat bei mir die Sicherheit und Korrektheit der Daten immer Vorrang vor eventuell möglichen Optimierungen.

Wenn die Quelle also ein System mit BDE ist, dann wird die Migration mit der BDE gemacht, nichts kann besser unter Delphi mit Paradox umgehen, wie die BDE. Dann lasst sie doch einfach noch einmal zeigen, was in ihr steckt.

Und die weiter oben genannte Komponente TBatchCopy ist ja gerade für solche Aufgaben da. Über die BDE auf Paradox zugreifen und über die BDE auf alles was sie selbst kann bzw. über den Weg BDE-ODBC.

Fehlerbehandlung ist bei der Komponente inclusive.

jobo 20. Dez 2016 19:12

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von nahpets (Beitrag 1356747)
..
Nicht nur ein Delphi, dass noch die BDE mitbringt, sondern auch ein Windows, auf dem die BDE garantiert noch problemlos läuft.
..

Das dürfte sich vermutlich vor Ort beim Kunden von allein ergeben.

Zitat:

Zitat von nahpets (Beitrag 1356747)
..
Das war irgendwo vor Windows XP mit Delphi 7?
..

Ob man gleich so weit zurück muss?

Vielleicht sind ja geeignete Win 7 Installationen am Start oder ein Windows 2003 Server mit "Restgarantie". Irgendwie werden sie ja mit dem System noch arbeiten. Ein Vorrat an funktionierenden PC, die das klaglos und fehlerfrei machen, sollte also da sein. Sowas würde ich dann nehmen.

Wie auch immer, die Idee ist sicher klar geworden

nahpets 20. Dez 2016 19:33

AW: violation of FOREIGN KEY constraint
 
Sagen wir mal so: Unter XP hab' ich ab und an Fehler mit der BDE bekommen, die Konfiguration läuft nicht zwingend sauber.

Aber seien wir mal ehrlich:

Als wird per VM gemacht, warum dann da nicht mal eine mit 'nem älteren Windows aufsetzen?

Dann hat man ggfls. 'nen aktuellen und schnellen Rechner und trotzdem ein "altes" Windows. Und 'ne ältere Windows-CD wird man hoffentlich noch finden ;-)

Und wenn man nicht alle Kunden am gleichen Wochenende migriert, kann man sich ggfls. auch 'nen passenden Laptop für genau diesen Job fertig machen und von Kunde zu Kunde mitschleppen.

Ein ehemaliger Arbeitgeber hat noch 'ne Software mit Delphi 4, BDE und DBase-Dateien "am Laufen". Das sollte mal auf einen neueren Rechner. Mit Windows 7 war das aber nicht mehr stabil hinzubekommen. Also hat der neue Rechner ein "altes Windows" bekommen und läuft nun stabil.

An 'ne Datenmigration traut sich leider keiner ran.

Zwirbel 20. Dez 2016 21:43

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von jobo (Beitrag 1356745)
Zitat:

Zitat von Zwirbel (Beitrag 1356660)
..
Ja, habe mir diese alten BDE-Komponenten für Delphi Seattle geholt. Der Versuch über ODBC zuzugreifen und damit die BDE aus der Migrations-Applikation heraus zu halten scheitert daran, dass wir ausschließlich Paradox Tabellen im 7er Format einsetzen und das geht über ODBC nicht.

..

Was wollt Ihr damit bezwecken, die BDE da raus zu halten?

Das wollten wir nicht, es war nur ein Vorschlag in einem anderen Beitrag per ODBC, statt mit der BDE, auf die alten zu migrierenden Paradox-Daten zu zuzugreifen.

Zitat:

Es gibt nichts besseres als nativen Zugriff. Wem nützt der None-BDE Versuch, wenn die Einsatzbereiche eh alle BDE-"verseucht" sind? Ich würde es eher umgekehrt machen und eine entsprechend alte (alt genug), native Delphi Version nehmen. Das verspricht die wenigsten Fehlerquellen.
Eine Datenkopiermaschine sollte man auch mit alten Delphis noch gut hinbekommen oder?
Kommt auf das Alter des alten Delphis an. :-D Das stammt aus dem letzten Jahrtausend, mehr sage ich nicht. Da wüsste ich jetzt nicht, wie ich bequem mit dem ollen Delphi Daten in eine FB-Datenbank schaufel. So wie im Moment scheint mir das ganz pragmatisch, neues Delphi mit aktuellen Schnittstellen zur neuen Ziel-Datenbank und als Zugriff in die alte Datenwelt noch die nativen BDE-Komponenten. Das scheint mir der beste Kompromiss zu sein.

Zwirbel 20. Dez 2016 21:53

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von nahpets (Beitrag 1356751)
Sagen wir mal so: Unter XP hab' ich ab und an Fehler mit der BDE bekommen, die Konfiguration läuft nicht zwingend sauber.

Aber seien wir mal ehrlich:

Als wird per VM gemacht, warum dann da nicht mal eine mit 'nem älteren Windows aufsetzen?

Dann hat man ggfls. 'nen aktuellen und schnellen Rechner und trotzdem ein "altes" Windows. Und 'ne ältere Windows-CD wird man hoffentlich noch finden ;-)

Und wenn man nicht alle Kunden am gleichen Wochenende migriert, kann man sich ggfls. auch 'nen passenden Laptop für genau diesen Job fertig machen und von Kunde zu Kunde mitschleppen.

Ein ehemaliger Arbeitgeber hat noch 'ne Software mit Delphi 4, BDE und DBase-Dateien "am Laufen". Das sollte mal auf einen neueren Rechner. Mit Windows 7 war das aber nicht mehr stabil hinzubekommen. Also hat der neue Rechner ein "altes Windows" bekommen und läuft nun stabil.

An 'ne Datenmigration traut sich leider keiner ran.

Die Option mit einem eigenen Rechner/Notebook zum Kunden aufzubrechen scheidet für uns definitiv aus. a) Sitzen die Kunden von Portugal, Spanien, Österreich, Schweiz, Deutschland auch, über Dänemark bis nach Russland und b) lässt mich keine IT-Abteilung mit einem fremden Rechner in deren Netz, die würden mich alle vorher erschießen. :-D Und ja, USB-Stick scheidet auch aus. :-D Alle Daten der Kunden müssen auf deren Equipment migriert werden. Und das muss möglichst alles voll automatisch laufen.

nahpets 20. Dez 2016 22:19

AW: violation of FOREIGN KEY constraint
 
Ok, ihr habt da schon etwas größere Baustellen.

Eigene Erfahrung:

Delphi 7 (aus dem Jahr 2002) und BDE funktioniert problemlos.

Delphi 7 und Oracle, MSSQL, PostGres, Ingres, MySQL, Firebird funktioniert problemlos.

Welches alte Delphi habt ihr? Über ODBC kann man eigentlich problemlos auch auf Firebird zugreifen, ist vielleicht etwas langsamer als über andere Wege, aber bisher hab' ich keine Probleme in der Form "Daten kommen nicht an, Daten kommen falsch an oder gar Zugriff nicht möglich." gehabt.

Und bei der Migration bitte den Weg über Scripte nicht auslassen.

Scripte kann man aus Delphi heraus per BDE-Zugriff auf Paradox erstellen.

ISQL kann man ggfls. auch aus dem Delphiprogramm heraus per ShellExecute ausführen.
Wenn man's mit Chromleiste will, kann man die Ausgabe von ISQL auch ins Programm übernehmen, so dass man hier das Delphiprogramm durchaus als alleiniges Migrations- und Steuerprogramm für die Datenübernahme gestalten kann.

Aber für genauere Tipps, Anregungen ... müsste man sich wohl doch etwas genauer in das Projekt und seine Bedingungen einarbeiten.

jobo 21. Dez 2016 08:01

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von Zwirbel (Beitrag 1356758)
Kommt auf das Alter des alten Delphis an. :-D Das stammt aus dem letzten Jahrtausend, mehr sage ich nicht. Da wüsste ich jetzt nicht, wie ich bequem mit dem ollen Delphi Daten in eine FB-Datenbank schaufel. So wie im Moment scheint mir das ganz pragmatisch, neues Delphi mit aktuellen Schnittstellen zur neuen Ziel-Datenbank und als Zugriff in die alte Datenwelt noch die nativen BDE-Komponenten. Das scheint mir der beste Kompromiss zu sein.

Ja, mit den BDE Komponenten ergibt es m.E. mehr Sinn. Wenn Ihr das so macht, ist meine Frage, warum ohne BDE ja auch eigentlich überflüssig.

Was nun das Alter der Delphiversion angeht, kann ich nur sagen, wir haben hier alte Anwendungen aus D5, die auch prima mit Oracle 12 arbeiten.

Exkurs
Und was bedeutet schon letztes Jahrtausend? Ich wohne in einem Haus aus dem letzten Jahrtausend, bis vor 2 Jahren fuhr ich ein Auto aus eben diesem Jahrtausend.
Ich habe Delphi immer geschätzt, weil es ein hohes Maß an Kontinuität in eine Welt brachte, in der MS das ganz anders handhabte.
Nur weil MS uns gelehrt hat, dass nichts so alt ist, wie die Software, die man dort gestern gekauft hat (oder geschenkt bekam), ist das bloße Alter einer Software für mich kein Kriterium.
Neu ist ja nicht per se besser, geschweige ausgereift und entbugged.
Aus der Perspektive würde ich reife Software gegenüber der neuen bevorzugen.

mkinzler 21. Dez 2016 09:18

AW: violation of FOREIGN KEY constraint
 
Ich würde mir überlegen ob es sinnvoll ist, nur zur Übernahme die BDE zu installieren.
Ich würde die Übernahme zudem in ei eigenes Hilfsprogramm auslagern.

Jumpy 21. Dez 2016 09:38

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von mkinzler (Beitrag 1356806)
Ich würde mir überlegen ob es sinnvoll ist, nur zur Übernahme die BDE zu installieren.
Ich würde die Übernahme zudem in ei eigenes Hilfsprogramm auslagern.

So wie ich das verstanden habe ist die BDE doch bei den Kunden noch im Einsatz und somit installiert. Das ist dann ja der Ort wo jeweils die Datenmigration stattfinden soll. Zumindest hab ich den TE so verstanden.
Allerdings würde ich auch empfehlen ein Extra-Tool dafür zu benutzen (oder Skripte oder so), da dies ja ein (pro Kunde) einmaliger Vorgang ist (hoffentlich).

Zwirbel 22. Dez 2016 06:58

AW: violation of FOREIGN KEY constraint
 
Zitat:

Zitat von Jumpy (Beitrag 1356817)
Zitat:

Zitat von mkinzler (Beitrag 1356806)
Ich würde mir überlegen ob es sinnvoll ist, nur zur Übernahme die BDE zu installieren.
Ich würde die Übernahme zudem in ei eigenes Hilfsprogramm auslagern.

So wie ich das verstanden habe ist die BDE doch bei den Kunden noch im Einsatz und somit installiert. Das ist dann ja der Ort wo jeweils die Datenmigration stattfinden soll. Zumindest hab ich den TE so verstanden.
Allerdings würde ich auch empfehlen ein Extra-Tool dafür zu benutzen (oder Skripte oder so), da dies ja ein (pro Kunde) einmaliger Vorgang ist (hoffentlich).

Ja, die BDE ist bei allen Kunden wo migriert wird installiert, das erfordert also keine extra Arbeit. Das Migrations-Tool ist eine separate Applikation und hat mit dem eigentlichen Produkt, das dann nur noch die FB-Datenbank nutzt, nichts zu tun. Das neue Programm das die eigentliche Datenverarbeitung auf der FB-Datenbank durchführt ist dann BDE-befreit. :)

p80286 22. Dez 2016 08:09

AW: violation of FOREIGN KEY constraint
 
Ich halte die BDE-Diskussion für überflüssig. Wenn ich die Ausgangsfrage richtig verstanden habe, enthält die Ausgangsdatenbank Tabelleneinträge, die "in der Luft schweben". Das könnte durch unvollständige Löschaktionen verursacht worden sein. Was auch immer der Grund war, diese Daten müssen bereinigt werden. Dies kann auf zwei Wegen erfolgen, zum einen ein Export der Daten durch vollständige Datensätze (select..join), hierbei tauchen diese Einträge nicht mehr auf, oder aber durch einen 1:1 Export, wobei die Datenbereinigung auf dem Zielsystem erfolgt. Hierbei wäre der Vorteil, daß die Daten des Altsystems auf dem neuen System vorhanden sind und Nachkonvertierungen relativ problemlos sind. Daß die Daten dann physisch doppelt vorliegen halte ich für nicht so gravierend.

Gruß
K-H


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:53 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz