![]() |
Datenbank: Interbase • Version: 6.01 • Zugriff über: IBX
Datensätze vergleiche
Hallo,
ich hänge immernoch an meinem Problem mit den 800.000 Datensätze. Nun hänge ich an einer anderen Stellen, da nun die Wünsche geändert wurden. Ich beschreibe mal die Gegebenheiten und mein Problem. Villeicht könnt Ihr mir ja nochmals helfen. Als Ergebnis sollte herrauskommen: Welche sind neue Daten und bei welchen hat sicht etwas geändert. Es liegen zwei CSV Dateien mit jewals fast 800.000 Datenstzäte vor. Diese beiden Dateien habe ich ich zwei Tabellen im Interbase geschrieben. (Kein Problem geht sehr zügig) Die 1. Tabelle sind so aufgebaut Feld1 VarChar(150) Character Set ISO8859_1 Feld2 VarChar(150) Character Set ISO8859_1 Fled3 VarChar(40) Character Set ISO8859_1 Feld4 VarChar(3) Character Set ISO8859_1 Feld5 VarChar(20) Character Set ISO8859_1 Fled6 VarChar(10) Character Set ISO8859_1 Die 2. Tabelle sieht genau so aus hat jedoch noch ein Feld für ein Datum In der 1. Tabelle sind die alten Daten und in der 2. Tabelle sind die neuen Daten. Nun wollte ich in alle Datensätze in der Tabelle2 das aktuelle Datum schreiben, wenn die Datensatz in der Tabelle1 vorhanden ist. Somit müßte der rest ohne Datum dann neu sein oder geändert worden. Dazu habe ich es zuerst versucht mit einen Update
SQL-Code:
Das ganze lief zwei Stunden und es hat sich nicht getan. Da habe ich es mal abgebrochen, da es villeicht noch Tage dauern kann bis das Ergebnis vorliegt.
Update Tabelle2
Set Datumsfeld = '01.08.07' where Feld1 || Feld2 || Feld3 || Feld4 || Feld5 || Feld6 in (Select Feld1 || Feld2 || Feld3 || Feld4 || Feld5 || Feld6 From Tabelle2) Dann habe ich auf jedes Feld einen Index gesetzt. Einen Index über alle Felder geht ja leider nicht, da er ja zu "breit" ist. Leider Das ganze mit den Abgleich kann auch genre 10 Stunden dauern nur das Ergebnis sollte stimmen. Also dacht ich mir mach es halt über ein Schleife. Ja mir ist bekannt es ist das schlechteste ist was ich machen kann. Alle Datensätze der Tabelle 1 hole ich in eiene TIBQuery und mittels einer Schleife suche dann mittels einer TIBQuery den Datensatz in Tabelle2. Wenn das Ergebnis nicht Leer ist setzte ich das Datum. Wenmn ein Feld nicht übereinstimmt wurde etwas geändert. Das ganze geht auch, würde jedoch ca. 123 Stunden dauern und das ist mir etwas zu lange. Kann mir jemand hier einen Tip geben wie ich es schneller hinbekommen werden. Man die Leute haben wünsche. Sage mir mal den Unterschied bei 800.000 Datensätzen. :wall: :gruebel: Tanja |
Re: Datensätze vergleiche
Der Concat scheint sehr langsam zu sein. Versuchs mal mit dem ausführlichen Statement
|
Re: Datensätze vergleiche
Ich würde es so versuchen (ungetestet):
SQL-Code:
UPDATE Tabelle2 B
SET B.Datumsfeld = '01.08.2007' WHERE EXISTS( SELECT * FROM Tabelle1 A WHERE A.Feld1 = B.Feld1 AND A.Feld2 = B.Feld2 AND A.Feld3 = B.Feld3 AND A.Feld4 = B.Feld4 AND A.Feld5 = B.Feld5 AND A.Feld6 = B.Feld6) |
Re: Datensätze vergleiche
Hallo,
SQL-Code:
Hast du das falsch abgetippt,
Update Tabelle2
Set Datumsfeld = '01.08.07' where Feld1 || Feld2 || Feld3 || Feld4 || Feld5 || Feld6 in (Select Feld1 || Feld2 || Feld3 || Feld4 || Feld5 || Feld6 From Tabelle2) es muss doch heissen in ( XXXFrom Tabelle1) statt Tabelle2 Nun zum Speed Ich würde in beide Tabellen ein SumFeld Varchar(XXX) (Summe aller Felder) eintragen, es per Update füllen (Update Table1 Set SumFelds = Field1 || Field 2 || ...)
SQL-Code:
Das Exisst sollte das noch etwas schneller machen.
Update Tabelle2
Set Datumsfeld = '01.08.07' where Tabelle2.SumFeld1 not exists in (Select Tabelle1.SumFeld From Tabelle1 Heiko PS: Die beiden SumFeld können danach ja wieder gelöscht werden. |
Re: Datensätze vergleiche
Hallo Tanja,
eine weitere Möglichkeit: Vor dem Laden der Dateien ein Feld mit dem Hash-Wert aller Feldinhalte hinzufügen - oder auch nach dem Laden über eine MD5 UDF, einen Index auf den Hash setzen und ein UPDATE mit dem bereits gezeigten Sub-Select. Freundliche Grüße |
Re: Datensätze vergleiche
Zitat:
Zitat:
Zitat:
Tanja PS: Die beiden SumFeld können danach ja wieder gelöscht werden.[/quote] |
Re: Datensätze vergleiche
hallo tanja,
sag mal, sind die datensätze sortiert? falls ja, brauchst du die tabellen nicht in eine datenbank zu schreiben, sondern nur die beiden tabellen lesen, durchschleifen und die unterschiede ausgeben... sollte programmmässig in ein paar sekunden durchgelaufen sein. aber die tabellen müssen nach dem primärschlüssel sortiert sein. sonst musst du immer die ganze tabelle durchhangeln und das geht in die zeit... grüsse gg |
Re: Datensätze vergleiche
Hier bietet sich z.B. Beyond Compare an, ein wirklich gutes Tool für diesen Zweck.
|
Re: Datensätze vergleiche
Hallo
argzz where not exists (select bla) es heisst nicht not exists in (select() sondern not exists (select() Heiko |
Re: Datensätze vergleiche
Hallo,
ich möchte nur noch anmerken, dass man mit der zu Beginn erwähnten Concat-Lösung eine Änderung an einem Datensatz evtl. nicht findet, obwohl diese stattgefunden hat. Beispiel: alter Datensatz: Feld1=ABC Feld2=DEF ... neuer Datensatz: Feld1=AB Feld2=CDEF ... hier hat sich was geändert, aber die Änderung würde anhand eines Vergleichs von Feld1 || Feld2 nie festgestellt werden ! Also, falls so was in der Praxis vorkommen kann, kann ein Vergleich mittels || nicht das gewünschte Ergebnis liefern. beha |
Re: Datensätze vergleiche
Na ja, dann packt man ein Trennzeichen zwischen die einzelnen Felder, das sonst im Text nicht vorkommt. Es ist sowieso performanter, einen Hash zu erzeugen, und diesen für einen Vergleich heranzuziehen.
|
Re: Datensätze vergleiche
Zitat:
mein Interbase 6 kennt kein exists. Ich bekomme immer die Fehlermeldung Dynamic SQL Error SQL error code = -104 Token unknown - line 2, char 14 exists Tanja |
Re: Datensätze vergleiche
Zitat:
Tanja |
Re: Datensätze vergleiche
Zitat:
kannst du mir sagen so ich so einen UDF bekommen kann und wie "Breit" dann der Hash-Wert ist. Dann würde ich es in eine neue Spalte einfügen und einen Index erzeugen. Dann sollte es ja schneller gehen. Danke Tanja |
Re: Datensätze vergleiche
Hallo Tanja,
nimm mal eine kleine Tabelle und probier das mal aus
SQL-Code:
Im Bsp ist Personal die Tabelle und Id der PrimKey.
select * from
personal where not exists (select id from personal) Das muss auch unter IB6 gehen. btw: welche Version von IB6 ist drauf ? Heiko |
Re: Datensätze vergleiche
Zitat:
ich habe hier die alte Version 6. Mit dem oben vor Dir geschrieben Select geht es zum Teil. Es kommt keine Fehlermeldung mehr wenn nicht kein Feldname hinter dem Where eingebe. Nur leider ist das Ergebis auch falsch, es kommt immer NULL und keine Ergebnismenge. Tanja |
Re: Datensätze vergleiche
Zitat:
|
Re: Datensätze vergleiche
Zitat:
Tanja |
Re: Datensätze vergleiche
Also:
1. Ein UDF ist eine 'User Defined Function'. Wie das genau mit IB6 geht, weiss ich nicht, aber man wird dir bestimmt helfen. Dessenungeachtet reicht es u.U. aus, für beide Tabellen einen Index auf ein Feld zu setzen, von dem man ausgehen kann, das es fast nur unterschiedliche Werte enthält (Den Namen z.B.). Dann vergleicht man beide Tabellen mit einem Join. Das Liefert in relativ kurzer Zeit alle identischen Zeilen.
SQL-Code:
Wenn man nur die Unterschiede möchte, dann verwendest Du einfach einen Full Join:
Select *
from Tabelle1 Join Tabelle2 on Tabelle1.IndexFeld = Tabelle2.IndexFeld and Tabelle1.Feld1 = Tabelle2.Feld1 and Tabelle1.Feld2 = Tabelle2.Feld2 ...
SQL-Code:
willst Du nur die Datensätze, die in Tabelle1 ABER NICHT in Tabelle2 sind, dann einen LEFT JOIN
Select *
from Tabelle1 Full Join Tabelle2 on Tabelle1.IndexFeld = Tabelle2.IndexFeld and Tabelle1.Feld1 = Tabelle2.Feld1 and Tabelle1.Feld2 = Tabelle2.Feld2 ... where Tabelle1.IndexFeld is null or Tabelle2.IndexFeld is null
SQL-Code:
Das sollte auch bei 800.000 Zeilen in vertretbarer Zeit durchzuführen sein. Bei SQL gilt: Probieren und Indexieren.
Select *
from Tabelle1 LEFT Join Tabelle2 on Tabelle1.IndexFeld = Tabelle2.IndexFeld and Tabelle1.Feld1 = Tabelle2.Feld1 and Tabelle1.Feld2 = Tabelle2.Feld2 ... where Tabelle2.IndexFeld is null Um nun die Zeilen eindeutig zu identifizieren, solltest du ihnen vorher eine 'AutoInc'-Spalte, also einen PrimaryKey verpassen. |
Re: Datensätze vergleiche
Ein IMHO gute Seite zu dem Thema ist
![]() |
Re: Datensätze vergleiche
Hallo Tanja,
Zitat:
![]() ![]() Es ist eine hohe Kunst eine Aufgabenstellung so zu beschreiben, dass die Hilfestellung eines Unbeteiligten keinen schalen Beigeschmack hinterlässt. Bei deinem Problem irritiert mich, dass du zwei Textdateien vergleichen sollst (was man mit drei Unix Text Tools schnell erledigt hat) und diese Dateien im CSV-Format zu dir gelangen (was bedeutet, dass sie wohl Export-Dateien sind). Auch weiß ich nicht, ob du diesen Vergleich regelmäßig oder einmalig durchführen willst und wie du das Ergebnis zurückgeben sollst. Ich gehe also einfach weiter davon aus, dass du lediglich ein performantes UPDATE-Statement für Interbase 6 suchst. Für eine optimale Lösung deines Problems müsste man statistische Informationen über deine Daten haben, wie alzaimar versteckt angedeutet hat. Aber alles in allem sollten zwei mal 1 Mio Datensätze noch kein richtiges Problem darstellen. Um der Ungewissheit des Laufzeitverhaltens zu entgehen, würde ich zwei samples (1% und 5%) erstellen und mit denen die Zeit nehmen. Danach weißt du wie dein UPDATE-Statement skaliert und kannst die Wartezeit für den full set abschätzen und dein performance tuning anpassen.
SQL-Code:
Freundliche Grüße
UPDATE tabelle2
SET datum = CURRENT_DATE() WHERE hash NOT IN (SELECT hash FROM tabelle1) |
Re: Datensätze vergleiche
Zitat:
Ich habe mir die UDF mal gezogen und werde es mal prüfen. Die Daten würde ich schön öfters bekommen und auswerten. Das Ergebnis sollte dann in der Datenbak weiter verarbeitet werden. Tanja |
Re: Datensätze vergleiche
Hallo Tanja,
mein SQL-Code sllte ja nur zeigen, dass Interbase6 sehr wohl das exists kennt. Die Nutzung von ist angeblich schneller als IN. Zur Performance. Warum einen Hash erzeugen, wenn es mit Concat (wie in meinem Bsp mit dem SumFeld) auch geht. OK, das mit dem einfachen Zusammenziehen war nicht so gut, aber ein feld1 || , feld2 || , sollte doch reichen ? Heiko |
Re: Datensätze vergleiche
Zitat:
ja es reicht schon, jedoch habe ich es nach ca. 2 Stunden abgebrochen. Es kann ja noch Tage laufen bzw. in 5 Minuten fertig sein. Tanja |
Re: Datensätze vergleiche
Hallo Tanjy,
was dauert denn so lange, schon das Erzeugen von SumFeld ? oder die nachfolgende Query ? Heiko |
Re: Datensätze vergleiche
Zitat:
das Summenfled ist kein Problem. Dauert ca. 3-5 Minuten. Nur dann die Query habe ich nach 2 Stunden mal abgebrochen. Tanja |
Re: Datensätze vergleiche
Hallo Tanja,
1. zeig noch mal die Query 2. nimm etwa 1000 Einträge pro Tabelle und teste die Query im IBPlanalyzer Heiko |
Re: Datensätze vergleiche
Zitat:
Zitat:
SQL-Code:
Nicht getestet ! So würde ich zumindest mal anfangen. :mrgreen:
CREATE PROCEDURE NEUTABLESP (
ID, FELD1 VARCHAR (... ... FELD6 As Declare Variable VORHANDEN Integer; BEGIN VORHANDEN = -1; SELECT ID FROM TABLE1 WHERE (FELD1= :FELD1) AND ... (FELD6= :FELD6) INTO :VORHANDEN; IF (VORHANDEN >= 0) THEN BEGIN UPDATE TABLE2 SET DATUM=CURRENT_TIMESTAMP WHERE (FELD1= :FELD1) AND ... (FELD6= :FELD6) END SUSPEND; END^ |
Re: Datensätze vergleiche
@hansa, die sortierung spielt soweit eine rolle, dass bei zwei unsortierten datensätzen, die eine datei immer wieder vom beginn an gelesen werden muss, was sich auf die laufzeit niederschlägt. wenn beide sortiert sind, dann braucht jede tabelle exact 1x durchlaufen werden und alle änderungen, inserts, deletes sind ermittelt.
@Dumpfbacke, dann kannste ja auch die sortierfunktion deiner datenbank nutzen, wenn du die flat files nicht sortieren möchtest. da brauchste aber zwei cursor nach folgendem schema...
SQL-Code:
und
select * from tab1 order by ...
SQL-Code:
wobei die sortieroptionen die selben sidn. und dann einfach wie bei zwei flat files durchschleifen... ein durchlauf über tab1 und die tab2 als slave und wenn du am ende angekommen bist, haste alle infos.
select * from tab2 order by ...
grüsse gg |
Re: Datensätze vergleiche
Wozu soll denn da die Sortierung eine Rolle spielen ? Wozu soll das gut sein ? :gruebel: Nochmals grob mein Vorschlag : sequentiell über Tabelle 2 wandern. Bei jedem Datensatz anhalten und die SP ausführen. Die soll in Tabelle 1 gucken, ob ein gleicher Datensatz existiert oder nicht. Falls ja => aktuellen Datensatz von Tabelle 2 updaten (Datum setzen) -> nächster DS von Tabelle 2. Selbes Spielchen durchführen bis EOF von Tabelle 2. DS, die in beiden Tabellen vorhanden sind haben dann ein Datum und die nicht in Tabelle 1 enthaltenen eben nicht. Und fertig. :mrgreen:
|
Re: Datensätze vergleiche
Prinzipiell hat Hansa recht. Nur ist IB/FB keine DBISAM-DB, sondern ein RDBMS. Hier wird mit Mengen gearbeitet. Die verhalten sich ohne Index intern genauso wie eine DBISAM, aber bei richtigen Indexen wird es verdammt schnell. Dann greifen eben B-Tree-Algorithmen, die pro Datensatz gegen O(1) gehen, also nahezu unabhängig von der Anzahl der Datensätze ist. Bei 800.000 macht das schon den Unterschied zwischen Stunden und Sekunden aus. Ich traue mir zu, bei 1-2 Mio Datensätzen ein Self-Join in ein paar Sekunden hinzubekommen.
Aber mir is det sowieso allet ejal weil ick morjen in Uuuurlaub fliegen tu! :mrgreen: :mrgreen: |
Re: Datensätze vergleiche
@hansa: tja, weil man dann nicht mehr sortieren braucht. so 'n ascii file, sortiert das BS mit 'n paar parametern problemlos... und dann läuft der vergleich, auch ohne DB in 'n paar sekunden durch.
@alzaimar: mit den ständigen zugriff auf die DB, kann es schon länger brauchen. aber bei zwei sortierten sequentziellen dateien (master --> slave) ist die laufzeit O(n). er liest exakt 2x 800'000 datensätze und die sache ist erledigt. da brauchts auch keinen index oder so was ...nur sortiert müssen die beiden tabellen sein. einen solchen fall, hatte ich schon lange nicht mehr. aber damals mit COBOL lieb das ganze in 'n paar sekunden (auf 'ner alten architektur [286 war es glaub ich]) durch. ansonsten einfach mal nach den stichwörtern "normierte programmierung", "gruppenwechsel" oder "sequentielle dateiverarbeitung" suchen. da stehen die standardprobleme (vor der datenbankzeit) mit sicherheit ausführlich beschrieben... grüsse und noch 'n schönen abend gg |
Re: Datensätze vergleiche
Liste der Anhänge anzeigen (Anzahl: 2)
Mehr ist das nicht :
Delphi-Quellcode:
Inkl. übernommene Schreibfehler. :mrgreen: Im Anhang sind 2 DBs. Da drin ist auch die SP. TESTDB2.FDB ist der Ursprungszustand TESTDB.FDB der Zustand der DB nach Programmlauf. Und Grenzgänger : sortiert ist da nichts. :-D
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, DB, FIBDataSet, pFIBDataSet, FIBDatabase, pFIBDatabase, FIBQuery, pFIBQuery, pFIBStoredProc; type TForm1 = class(TForm) Database: TpFIBDatabase; Transaction: TpFIBTransaction; Test1DS: TpFIBDataSet; Test2DS: TpFIBDataSet; Button1: TButton; Memo1: TMemo; TestSP: TpFIBStoredProc; procedure Button1Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var i : integer; begin i := 0; Test1DS.close; Test1DS.SelectSQL.Text := 'SELECT * FROM TEST1'; Test1DS.Open; while not Test1DS.Eof do begin i := i + 1; TestSP.ParamByName('Feld1').AsString := Test1DS.FieldByName ('Feld1').AsString; TestSP.ParamByName('Feld2').AsString := Test1DS.FieldByName ('Feld2').AsString; TestSP.ParamByName('Fled3').AsString := Test1DS.FieldByName ('Fled3').AsString; TestSP.ParamByName('Feld4').AsString := Test1DS.FieldByName ('Feld4').AsString; TestSP.ParamByName('Feld5').AsString := Test1DS.FieldByName ('Feld5').AsString; TestSP.ParamByName('Fled6').AsString := Test1DS.FieldByName ('Fled6').AsString; TestSP.ExecProc; Memo1.Lines.Add(IntToStr (i)); Test1DS.Next; end; Transaction.Commit; Database.Close; showmessage ('fertig !'); Close; end; end. |
Re: Datensätze vergleiche
klar hansa, es geht auch ohne sortieren. da erschlägt man dann das problem mit mehr rechenleistung :-) . aber das hat man ja heutzutage :-)
die lösung mit den zwei sortierten flat files, kommt mit vielleicht 50kb hauptspeicher aus und braucht keine datenbank sondern nur zwei open und readln... ;-) das ging etwa so...
Delphi-Quellcode:
das hier wär da auch schon der ganze code ;-) . also so komplex ist das mit den zwei sortierten flat fies gar nicht und da sortiert auch sehr schnell ;-)
open master; open slave;
master.first; slave.first; while not master.eof do begin if master.record > slave.record then begin writeln('gelöschter slave record: ', slave.record); slave.next; end else if master.record = slave.record then begin writeln('master record = slave record'); slave.next; end else begin //master.record < slave.record writeln('neuer slave record', slave.record); end; master.next; end; while not slave.eof do //noch datensätze in slave übrig? begin writeln('neuer slave record: ', slave.record); slave.next; end; close master; close slave; grüsse und noch 'n schönes weekend gg |
Alle Zeitangaben in WEZ +1. Es ist jetzt 15: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-2025 by Thomas Breitkreuz