Entwickler-Ecke
Datenbanken - Suche in Datenbank dauert unterschiedlich lange
Delete - Mi 25.07.12 09:30
Titel: Suche in Datenbank dauert unterschiedlich lange
Moin allerseits,
ich durchsuche in meiner Firebird-Datenbank eine Tabelle nach einem String ab, der sich in drei verschiedenen Spalten befinden kann, einem VarChar-Feld und zwei Blobfeldern – das nur mal zum Einstieg.
Wenn ich hintereinander 20 mal denselben Suchbefehl absetze, dauert das Durchsuchen von genau 200 Datensätzen zwischen 2577 Millisekunden (ms) und 5594 ms (im Schnitt 3316,6 ms). Wie kommen diese (für einen DoubleCore-Rechner mit je 3 GHz) doch immense Unterschiede zustande?
Also nicht die Dauer ansich soll hier das Thema sein (ich könnte z.B. die ProgressBar rausschmeißen), sondern die unterschiedlichen Suchzeiten, das sind ja gut 3 Sekunden mehr oder weniger ... wie sieht das dann erst bei 2000 oder 20.000 Datensätzen aus?
Die Suchfunktion im einzelnen ist zur Beantwortung der Frage wohl eher zweitrangig, aber dennoch (verkürzte Darstellung):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31:
| VAR Fund : Boolean; Suchwort, Inhalt : String;
BEGIN ... Dataset.First;
WHILE NOT Dataset.Eof DO BEGIN Fund := FALSE; Inhalt := Dataset.FieldByName('TITEL').asString; Fund := ContainsStr(Inhalt,Suchwort);
IF NOT Fund THEN BEGIN Inhalt := Dataset.FieldByName('Notiz').asString; Fund := ContainsStr(Inhalt,Suchwort); END;
IF NOT Fund THEN BEGIN Inhalt := Dataset.FieldByName('Code').asString; Fund := ContainsStr(Inhalt,Suchwort); END;
Dataset.Next; END; ... END; |
Moderiert von
Narses: Topic aus Sonstiges (Delphi) verschoben am Mi 25.07.2012 um 11:04
bummi - Mi 25.07.12 09:47
kennt Firebird keine "Like" oder "Contains" für Blobs?
Wenn es geht würde ich versuchen das ganze in SQL's auszulagern
Delete - Mi 25.07.12 10:04
bummi hat folgendes geschrieben : |
| kennt Firebird keine "Like" oder "Contains" für Blobs? |
Weiß ich nicht, werde ich recherchieren ...
bummi hat folgendes geschrieben : |
| Wenn es geht würde ich versuchen das ganze in SQL's auszulagern |
Keine Ahnung, ob das geht. Ich will ja kein SELECT durchführen. In den Datensätzen, die gefunden werden, wird ein Boolean-Feld auf 1 gesetzt.
Die Datensätze dieser Tabelle (neben zwei weiteren Tabellen) werden teilweise in einem VirtualTreeview dargestellt, die Blob-Felder dagegen jeweils in einem TSynEdit (TDBSynEdit ist leider nicht unicode-fähig) und in einem TjvDBRichRedit (DB-Richedit der Jedis wegen dem URL-Klick). Deshalb fällt das Durchsuchen des Trees aus, denn damit könnte ich nur die Bezeichner durchsuchen, aber nicht die Blobfelder. Für letztere muß ich in der Tabelle scrollen ... und das kostet Zeit.
Meine SQL-Kenntnisse reichen zwar aus, um Views zu erstellen, aber wohl noch nicht ganz, um die komplette Suche in der Datennbank ablaufen zu lassen. Ich werd mir jetzt erst mal den "Firebird Database Cache Buffer", den Blub erwähnt hat, zu Gemüte führen ... danach die von dir erwähnten "Like oder Contains" ... Merci
Daß ich die Suchfunktion noch optimieren kann/muß, war mir schon klar. Wie aber die großen zeitlichen Unterschiede beim Suchen zustande kommen, kann ich mir immer noch nicht erklären ...
bummi - Mi 25.07.12 10:27
wenn die erste Such lange dauert und es dann schneller geht der Cache, aber dann hättest Du wahrscheinlich nicht gefragt ....
Delete - Mi 25.07.12 10:35
bummi hat folgendes geschrieben : |
| wenn die erste Such lange dauert und es dann schneller geht der Cache, aber dann hättest Du wahrscheinlich nicht gefragt .... |
Nein, die Zeiten sind völlig durcheinander, anfangs waren es um die 3000 ms, zwischendring plötzlich 5500 und dazwischen auch mal 2500. Soagar 2500 ist natürlich zu lang, aber das ist ein anderes Problem, das ich noch zu lösen gedenke ...
Ich könnte mir aber vorstellen, daß vielleicht irgendwelche anderen Aktionen im Windows Rechenzeit beanspruchen – wer weiß schon, was Windows im Hintergrund so alles rumwurstelt. Ein paarmal stockte gar die Progressbar bemerkbar, wenn auch nur kurz.
Mich hat das eben grundsätzlich interessiert, weil ich gerade beim Optimieren der Suchfunktion bin (z.B. dauert die Suche mit POS und der Abfrage
Fund := POS > 0 etwas länger als die mit
Fund := ContainsStr(Inhalt,Suchwort): durchschnittlich 4000 ms.
Moderiert von
Narses: Beiträge zusammengefasstFortsetzung:
Nun habe ich erstmal das Dataset (FibPlus) in den Optionen auf FetchAll gestellt. Das heißt laut Dokumentation, daß alle Datensätze der Tabelle in den Speicher geladen werden. Das Suchen verläuft nun deutlich schneller, bei 20 Durchgängen im Schnitt 2555,15 ms statt wie vorher 3316,6 ms, der schnellste war 2157 ms und der langsamste 3874 ms. Ohne Progressbar läuft's auch nicht schneller, im Gegenteil (seltsam): durchschnittlich 2588,65 ms, der schnellste mit 2374 ms und der langsamste mit 3281 ms.
Aber immerhin! Da ist noch was rauszuholen ...
Nersgatt - Mi 25.07.12 11:58
Ist das Dataset noch an irgendwelche Controls gebunden? Dann wäre es vermutlich deutlich schneller, wenn Du DisableControls aufrufst:
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| DataSet.DisableControls; try ... Suche durchführen finally DataSet.EnableControls; end; |
Delete - Mi 25.07.12 12:01
Nersgatt hat folgendes geschrieben : |
Ist das Dataset noch an irgendwelche Controls gebunden? Dann wäre es vermutlich deutlich schneller, wenn Du DisableControls aufrufst:
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| DataSet.DisableControls; try ... Suche durchführen finally DataSet.EnableControls; end; | |
Danke, das hab ich bislang noch nicht probiert. Ich hatte immer die Datasources disabled, aber du hast vermutlich recht, denn die Datasourses sind ja noch immer mit den Datasets verbunden. Werde dir gleich berichten, ob's dadurch schneller wird.
Moderiert von
Narses: Beiträge zusammengefasstSo, der Testlauf ist durch: Ich habe 100 Durchläufe getestet,
der schnellste:
2171 ms
der langsamste:
6140 ms
Durchschnitt:
2995,16 ms
Also knapp 3 Sekunden für das Durchsuchen von drei Feldern in 200 Datensätzen nach einem einzigen Suchbegriff. Ist immer noch zu lang! Dein Vorschlag hat keine erkennbare Zeitersparnis gebracht – leider.
Aber ... wenn man den Ausreißer – so nennt man eine signifikante statistische Abweichung – wegläßt, dann ... :?!?: :?!?: :?!?: ... kommt man auf 2963,4 ms, auch nicht viel besser ...
Aber ich hatte eben beim Zuschauen meines Testdurchlaufs noch eine andere Idee: Ich setze jetzt mal AutoCommit des Datasets auf False und mache ein Commit erst am Ende, wenn alle Datensätze durchsucht sind. Mal schauen, ob das was bringt ...
Moderiert von
Narses: Beiträge zusammengefasst Perlsau hat folgendes geschrieben : |
| Aber ich hatte eben beim Zuschauen meines Testdurchlaufs noch eine andere Idee: Ich setze jetzt mal AutoCommit des Datasets auf False und mache ein Commit erst am Ende, wenn alle Datensätze durchsucht sind. Mal schauen, ob das was bringt ... |
Das hat's gebracht!!! :lol: :D :P :idea:
Vor dem Suchen
Dataset.AutoCommit := FALSE;
Nach dem Suchen
Delphi-Quelltext
1: 2:
| Dataset.StartTransaction; Dataset.AutoCommit := TRUE; |
Das Ergebnis bei wiederum 100 Suchläufen in allen drei Feldern über 200 Datensätze ... man lese und staune ...
Der schnellste:
281 ms
Der langsamste:
874 ms
Schnitt:
542,8 ms
Man sieht, es lohnt sich, die Sache etwas genauer zu durchleuchten. Nun ist mir auch die Lösung der Eingangsfrage erstmal egal, die da lautete: Wieso sind die Suchzeiten bei denselben Einstellungen immer unterschiedlich?
Blup - Mi 25.07.12 13:13
| Zitat: |
In den Datensätzen, die gefunden werden, wird ein Boolean-Feld auf 1 gesetzt.
|
Vermutlich ist das eine ganz schlechte Idee.
Durch das Update wird erst einmal eine Kopie des Datensatzes angelegt, davon ist in jeder Transaktion natürlich nur einer sichtbar. In deiner Transaktion der veränderte Datensatz, in älteren das Orginal. Aufgeräumt wird erst, wenn alle älteren Transaktionen beendet wurden.
| Zitat: |
... wie sieht das dann erst bei 2000 oder 20.000 Datensätzen aus?
|
Angenommen du setzt vor der Suche alle Boolean-Felder auf 0, wird im Hintergrund eine komplette Kopie aller Daten der Tabelle angelegt.
Wenn sich das Zwischenspeichern der Suchergebnisse nicht vermeiden lässt, dann besser in einer zusätzlichen Tabelle wo nur die ID der gefundenen Datensätze gespeichert wird.
Allerdings scheint mir das hier nicht notwendig zu sein, CONTAINING kann man auch auf TextBlobs anwenden.
Delete - Mi 25.07.12 13:32
Blup hat folgendes geschrieben : |
| Zitat: |
In den Datensätzen, die gefunden werden, wird ein Boolean-Feld auf 1 gesetzt.
|
Vermutlich ist das eine ganz schlechte Idee.
Durch das Update wird erst einmal eine Kopie des Datensatzes angelegt, davon ist in jeder Transaktion natürlich nur einer sichtbar. In deiner Transaktion der veränderte Datensatz, in älteren das Orginal. Aufgeräumt wird erst, wenn alle älteren Transaktionen beendet wurden. |
Nein, ich setze nicht erst bei allen Datensätzen das Booleanfeld auf 0, sondern weise dem Booleanfeld den Wert der Variablen Fund zu:
Delphi-Quelltext
1: 2: 3: 4:
| Fund := ContainsStr(Inhalt,Konst.Code_Suchwort); Dataset.Edit; Dataset.FieldByName('MARKIERT').AsBoolean := Fund; Dataset.Post; |
Erst wenn alle Datensätze durchsucht wurden, wird eine Transaktion gestartet.
Blup hat folgendes geschrieben : |
| Zitat: |
... wie sieht das dann erst bei 2000 oder 20.000 Datensätzen aus?
|
Angenommen du setzt vor der Suche alle Boolean-Felder auf 0, wird im Hintergrund eine komplette Kopie aller Daten der Tabelle angelegt. |
Und genau das mache ich nicht. Aber ich hab jetzt eben durch beim Lesen deines Beitrags einen Denkfehler aufgedeckt: Wenn ich z.B. nur eine bestimmte Datenmenge durchsuche (und das biete ich im Programm an, z.B. nur in der aktuellen Kategorie suchen), dann bleiben die bereits markierten, nicht zur aktuellen Kategorie gehörenden Datensätze ja weiterhin markiert. Das soll nicht sein. Ich werde jetzt gleich mal einen SQL-Befehl absetzen, der alle markierten Datensätze demarkiert und dann erst die Suche beginnen. Der SQL-Befehl müßte schnell ausgeführt sein, denke ich.
Blup hat folgendes geschrieben : |
| Wenn sich das Zwischenspeichern der Suchergebnisse nicht vermeiden lässt, dann besser in einer zusätzlichen Tabelle wo nur die ID der gefundenen Datensätze gespeichert wird. |
Das wäre auch eine Möglichkeit. Aber ich möchte den Markierungs-Status der aller Datensätze abspeichern, so daß dem Anwender beim nächsten Programmstart seine bereits gefundenen Datensätze wieder verfügbar sind. Dazu muß ich die Markierungen setzen, ob nun in der Original-Tabelle oder in einer virtuellen Zwischentabelle. Bei deinem Vorschlag wäre statt des Edit und Post ein Append und Post notwendig, ob das am Ende schneller ist, weiß ich nicht.
Blup hat folgendes geschrieben : |
| Allerdings scheint mir das hier nicht notwendig zu sein, CONTAINING kann man auch auf TextBlobs anwenden. |
CONTAINIG? Muß ich erstmal nachschlagen, was das heißt ...
Merci
Blup - Mi 25.07.12 15:32
| Zitat: |
Erst wenn alle Datensätze durchsucht wurden, wird eine Transaktion gestartet.
|
Das ist ein Irrtum, in der Datenbank geht nichts ohne Transaktionen. Jedes Lesen von Daten findet in einer Transaktion statt und natürlich auch jede Datenveränderung. Diese Transaktionen werden automatisch erzeugt durch die Datenkomponenten gestartet und beim Schließen der Datenmengen (hoffentlich) beendet. Wichtig ist in diesem Zusammenhang wie lange die Transaktionen offen sind (bzw. die älteste aktive Transaktion).
Wenn Komponenten zur Darstellung verwendet werden, die z.B. über Dataset direkt mit der Datenbank komunizieren, wird dazu eine Transaktionen gestartet, die möglicherweise erst beim Schließen des Formulars beendet wird.
| Zitat: |
Nein, ich setze nicht erst bei allen Datensätzen das Booleanfeld auf 0, sondern weise dem Booleanfeld den Wert der Variablen Fund zu: |
Wenn neben der Transaktion in der diese Datenänderung passiert, eine weitere Transaktion existiert, führt das genau zu der beschriebenen Datenverdoppelung. Als Folge könnte die Anwendung mit jeder Suchanfrage langsamer werden.
Delete - Mi 25.07.12 16:10
Blup hat folgendes geschrieben : |
| Zitat: |
Erst wenn alle Datensätze durchsucht wurden, wird eine Transaktion gestartet.
|
Das ist ein Irrtum, in der Datenbank geht nichts ohne Transaktionen. Jedes Lesen von Daten findet in einer Transaktion statt und natürlich auch jede Datenveränderung. Diese Transaktionen werden automatisch erzeugt durch die Datenkomponenten gestartet und beim Schließen der Datenmengen (hoffentlich) beendet. Wichtig ist in diesem Zusammenhang wie lange die Transaktionen offen sind (bzw. die älteste aktive Transaktion). |
Wo du recht hast ... Meine Einsichtsfähigkeit wird zum Wohle des Projekts nicht begrenzt, und wenn es noch so viel Schweiß und Tränen kostet :cry:
Nein, alles halb so wild. Ich hab mir jetzt die Empfehlung von bummi zu Herzen genommen:
| Zitat: |
| Wenn es geht würde ich versuchen das ganze in SQL's auszulagern |
Genau das hat mich zwar etliche Mühe gekostet und mein Nacken ist schon wieder völlig verspannt (weshalb ich jetzt auch Feierabend mache für heute), aber ich hab's geschafft und konnte danach haufenweise überflüssigen, sperrigen und unübersichtlichen Code (zumindest im Vergleich zum jetzigen) entsorgen. Hier die komplette Suchprocedure für alle, die damit was anfangen können
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101:
| procedure TFormCode.Bit_SuchStartClick(Sender: TObject); CONST L = '%';
VAR Anzahl : Integer; SQLtext, Suchwort : String; GK, Fund : Boolean; begin Start := Now;
IF Edit_Such.Text = '' THEN BEGIN ShowMessage('Vor dem Suchen bitte Suchtext eingeben ...'); EXIT; END ELSE BEGIN Konst.Code_Suchwort := Edit_Such.Text; Suchwort := Edit_Such.Text; END;
IF NOT DBCheck_Titel.Checked AND NOT DBCheck_Code.Checked AND NOT DBCheck_Notiz.Checked THEN BEGIN ShowMessage('Vor dem Suchen bitte einen Suchort markieren ...'); EXIT; END;
Anzahl := DatMod.Dset_CodeListe.RecordCount; IF Anzahl = 0 THEN BEGIN ShowMessage('Es existieren keine Einträge zum Durchsuchen ...'); EXIT; END;
DatMod.Dset_CodeListe.Close; DatMod.Trans_Update.Active := TRUE; DatMod.Query_Main.Close; DatMod.Query_Main.SQL.Clear; DatMod.Query_Main.SQL.Append('update CODELISTE'); DatMod.Query_Main.SQL.Append('set MARKIERT = 0'); DatMod.Query_Main.ExecQuery;
Ausblenden; GK := DBCheck_GrossKlein.Checked;
IF GK THEN Suchwort := L + Suchwort + L; Suchwort := QuotedStr(Suchwort);
IF DBCheck_Titel.Checked THEN IF NOT GK THEN SQLtext := '(TITEL containing ' + Suchwort +')' ELSE SQLtext := '(TITEL like ' + Suchwort +')';
IF DBCheck_Code.Checked THEN IF NOT GK THEN SQLtext := SQLtext + ' or (CODE containing ' + Suchwort +')' ELSE SQLtext := SQLtext + 'or (CODE like ' + Suchwort +')';
IF DBCheck_Notiz.Checked THEN IF NOT GK THEN SQLtext := SQLtext + ' or (BESCHREIBUNG containing ' + Suchwort +')' ELSE SQLtext := SQLtext + 'or (BESCHREIBUNG like ' + Suchwort +')';
DatMod.Dset_CodeListe.Close; DatMod.Trans_Update.Active := TRUE; DatMod.Query_Main.Close; DatMod.Query_Main.SQL.Clear; DatMod.Query_Main.SQL.Append('update CODELISTE'); DatMod.Query_Main.SQL.Append('set MARKIERT = 1'); DatMod.Query_Main.SQL.Append('where ' + SQLtext); DatMod.Query_Main.ExecQuery;
DatMod.Dset_CodeListe.Open; DatMod.Dset_CodeListe.Filtered := FALSE; DatMod.Dset_CodeListe.Filter := 'MARKIERT = 1'; DatMod.Dset_CodeListe.Filtered := TRUE; DatMod.Dset_CodeListe.Last; Anzahl := DatMod.Dset_CodeListe.RecordCount; DatMod.Dset_CodeListe.Filtered := FALSE;
Einblenden; Einlesen;
IF Anzahl = 0 THEN SQLtext := 'Keine passenden Einträge gefunden.'; IF Anzahl = 1 THEN SQLtext := IntToStr(Anzahl) + ' Eintrag gefunden.'; IF Anzahl > 1 THEN SQLtext := IntToStr(Anzahl) + ' Einträge gefunden.';
VST.SetFocus; Ende := Now; ShowMessage(SQLtext); |
Das macht bei 100 Durchläufen derselben Suche über alle 200 Datensätze im Schnitt 203,91 ms aus, davon der schnellste mit 124 ms und der langsamste mit 375 ms. Man kann sagen, es geht ungefähr 10mal so schnell wie mit der ursprünglichen Lösung.
Beim Verwenden von LIKE und CONTAINING muß man darauf achten:
LIKE is a case-sensitive pattern search operator. It is quite a blunt instrument
compared to pattern searches in regular expression engines. It recognizes two “wildcard”
symbols, % and _ (the underscore character), that work as follows:
• % can be substituted into a search string to represent any number of unknown
characters, including none, for example: LIKE '%mit%'
A CONTAINING search is case insensitive.
Das heißt, wenn man die Groß-/Kleinschreibung berücksichtigt, muß man mit LIKE suchen, will man sie vernachlässigen, mit CONTAINING.
Blup hat folgendes geschrieben : |
| Wenn Komponenten zur Darstellung verwendet werden, die z.B. über Dataset direkt mit der Datenbank komunizieren, wird dazu eine Transaktionen gestartet, die möglicherweise erst beim Schließen des Formulars beendet wird. |
Ja, und das macht die ganze Sache träge. Ich hatte früher schonmal einen Test gestartet, beim 130sten Durchlauf wurde der Suchvorgang immer langsamer ...
Für die Interessierten hier noch meine Test-Procedure:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42:
| procedure TFormCode.Test(Sender: TObject); VAR i,Wert : Integer; Liste : TListBox; Datei : String; Schnitt : Extended;
begin Liste := TListBox.Create(FormCode); Liste.Parent := FormCode; Liste.Visible := FALSE; Liste.Sorted := FALSE;
Datei := Konst.Pfad_Main + 'Zeittest.txt'; Wert := 0;
FOR i := 1 TO MaxTest DO BEGIN Bit_SuchStartClick(Sender); ZeitTest[i] := MillisecondsBetween(Start,Ende); Wert := Wert + ZeitTest[i]; Liste.Items.Append(IntToStr(ZeitTest[i]));
FormCode.Caption := IntToStr(i); Application.ProcessMessages; END;
Schnitt := Wert / MaxTest;
Liste.Items.Append(''); Liste.Items.Append(FloatToStr(Schnitt));
Liste.Items.SaveToFile(Datei);
Liste.Sorted := TRUE; Datei := Konst.Pfad_Main + 'Zeittest sortiert.txt'; Liste.Items.SaveToFile(Datei);
FreeAndNil(Liste); end; |
Delete - Do 26.07.12 04:38
Wie so oft, wenn ich übermüdet bin, habe ich (bei der Zusammenstellung des SQL-Textes) nicht ganz sauber gearbeitet. Wenn man nämlich die erste Suchoption (Suche im Titel) nicht aktiviert, gibt es einen Fehler, weil der SQL-Text mit "or" beginnt, was natürlich überhaupt keinen Sinn macht. Die korrigierte Version:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23:
| IF DBCheck_Titel.Checked THEN IF NOT GK THEN SQLtext := '(TITEL containing ' + Suchwort +')' ELSE SQLtext := '(TITEL like ' + Suchwort +')';
IF DBCheck_Code.Checked THEN BEGIN IF SQLtext <> '' THEN SQLtext := SQLtext + ' or '; IF NOT GK THEN SQLtext := SQLtext + '(CODE containing ' + Suchwort +')' ELSE SQLtext := SQLtext + '(CODE like ' + Suchwort +')'; END;
IF DBCheck_Notiz.Checked THEN BEGIN IF SQLtext <> '' THEN SQLtext := SQLtext + ' or '; IF NOT GK THEN SQLtext := SQLtext + '(BESCHREIBUNG containing ' + Suchwort +')' ELSE SQLtext := SQLtext + '(BESCHREIBUNG like ' + Suchwort +')'; END; |
kkausp - Do 26.07.12 14:10
Hallo,
und warum jetzt nicht die where Klausel "DatMod.Query_Main.SQL.Append('where ' + SQLtext);" direkt auf
DatMod.Dset_CodeListe anwenden?
Delete - Do 26.07.12 15:53
kkausp hat folgendes geschrieben : |
Hallo,
und warum jetzt nicht die where Klausel "DatMod.Query_Main.SQL.Append('where ' + SQLtext);" direkt auf
DatMod.Dset_CodeListe anwenden? |
Probier's doch einfach mal aus, dann weißt du die Antwort. Ich verwende übrigens die FibPlus-Komponenten, dort ist das Dataset etwas anders aufgebaut aus z.B. bei AdoDataset ...
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!