| Autor |
Beitrag |
dreamtheater
      
Beiträge: 46
|
Verfasst: Mi 18.06.03 10:57
Ich benutze eine kleine DB4 Datenbank. Wenn ich mit AppendRecord einen Datensatz hinzufüge, ihn später aber mit Delete wieder lösche und darauf wieder ein AppendRecord ausführe, so hat dieser nicht die RecNo von RecordCount {vorher} +1 sondern RecordCount {vorher} + 2. Das setzt immer so fort...
Beispiel:
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:
| // RecordCount = 3
AppendRecord
// RecordCount = 4
// RecNo=4
Delete
// RecordCount = 3 // RecNo=3
AppendRecord
// RecordCount = 4 // RecNo = 5 !!! (anstatt 4)
Delete
// RecordCount = 3 // RecNo=3
AppendRecord
// RecordCount = 4 // RecNo = 6 (anstatt 4) |
etc.
Was mache ich falsch?
|
|
CenBells
      
Beiträge: 1547
Win 7
Delphi XE5 Pro
|
Verfasst: Mi 18.06.03 11:17
Hallo,
das liegt daran, daß bei einem Delete ein datensatz nur als gelöscht markiert wird, aber nicht physikalisch gelöscht wird. Um das zu machen, musst du nach einem Delete die funktion pack aufrufen. Wird nicht von allen Datenbanken unterstützt, aber DB4 müsste hinhauen.
Gruß
Ken
|
|
grayfox
      
Beiträge: 800
win98, winXP
D4 Standard; D6 Personal
|
Verfasst: Mi 18.06.03 11:24
hallo dreamthaeter!
also ganz brandneu ist dein problem auch nicht!
es ergibt sich daraus, dass 'delete' den datensatz nicht aus der tabelle löscht, sondern ihn nur als 'gelöscht' markiert.
warum tut er sowas?
damit das löschen schneller erledigt ist, denn sonst müsste er alle darauffolgenden datensätze um einen platz nach vor rücken.
abhilfe schaffen kannst nur, indem du die tabelle 'packst'
hier findest eine packroutine --> DBase-Table packen
mfg, stefan
|
|
grayfox
      
Beiträge: 800
win98, winXP
D4 Standard; D6 Personal
|
Verfasst: Mi 18.06.03 11:25
das kommt davon, wenn man so lange nach einem passenden link sucht
mfg, stefan
|
|
dreamtheater 
      
Beiträge: 46
|
Verfasst: Mi 18.06.03 13:30
Hmm, klingt interessant, aber ich versth, das nicht ganz: was genau ist mit 'packen' gemeint? (unter 'packen' verstand ich bis jetzt sowas wie 'zippen')
Die Arbeitsweise der Funktionen
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:
| function dgPackParadoxTable(Tbl: TTable; Db: TDatabase): DBIResult;
var TblDesc: CRTblDesc; begin Result := DBIERR_NA; FillChar(TblDesc, SizeOf(CRTblDesc), 0); StrPCopy(TblDesc.szTblName, Tbl.TableName); TblDesc.bPack := True; Result := DbiDoRestructure(Db.Handle, 1, @TblDesc, nil, nil, nil, False); end;
function dgPackDbaseTable(Tbl: TTable): DBIResult;
begin Result := DBIERR_NA; if Tbl.Active = False then Tbl.Open; Result := DbiPackTable(Tbl.DBHandle, Tbl.Handle, nil, nil, True); end; |
respektive der 2., die ja für meinen Fall interessant sein sollte, ist mir auch etwas schleierhaft.
Könnte mir jemand genaueres erläutern?
(Ich benutze übrigens Delphi 3)
Danke,
dreamtheater
Moderiert von Marc: Code-Tag durch Delphi-Tag ersetzt.
|
|
dreamtheater 
      
Beiträge: 46
|
Verfasst: Mi 18.06.03 14:01
Die Routine dgPackDbaseTable scheint nicht zu funktionieren, vielleicht wende ich sie auch falsch an, aber wenn ich zuerst Delete und dann dgPackDbaseTable aufrufe, ändert sich an meinem Problem nichts - exakt dasselbe wie vorher...
|
|
hwb
      
Beiträge: 91
Win98 SE, Win2000
D5 Prof
|
Verfasst: Mi 18.06.03 15:02
Die Funktion geht:
Das ist ein komplettes Testprogramm mit der DemoDB Animals.dbf.
Wichtig: Unit BDE einbinden und das Table muß im EXCLUSIV-Modus
geöffnet sein ! Das geht nicht, wenn du die Tabelle schon unter der IDE offen hast.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, DBCtrls, Grids, DBGrids, Db, DBTables, StdCtrls, bde;
type
TForm1 = class(TForm)
Table1: TTable;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
DBImage1: TDBImage;
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormActivate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
function dgPackDbaseTable(Tbl: TTable): DBIResult;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
begin
Table1.Active := false;
Table1.Exclusive := true;
dgPackDbaseTable(Table1);
Table1.Active := false;
Table1.Exclusive := false;
Table1.Active := true;
end;
function dgPackDbaseTable(Tbl: TTable): DBIResult;
{ Pack a dBASE table by calling DbiPackTable. The table
passed as a parameter will be opened if it isn't open. }
begin
Result := DBIERR_NA;
if Tbl.Active = False then
Tbl.Open;
Result := DbiPackTable(Tbl.DBHandle, Tbl.Handle,
nil, nil, True);
end;
procedure TForm1.FormActivate(Sender: TObject);
begin
Table1.active := true;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Table1.Active := false;
end;
end.
Die Datei ist hinterher kleiner. (Dateimanager)
Gruß
HWB
|
|
dreamtheater 
      
Beiträge: 46
|
Verfasst: Di 24.06.03 18:33
Es funktioniert einfach nicht! Was mache ich falsch??
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:
| function dgPackDbaseTable(Tbl: TTable): DBIResult;
{ Pack a dBASE table by calling DbiPackTable. The table passed as a parameter will be opened if it isn't open. } begin Result := DBIERR_NA; if Tbl.Active = False then Tbl.Open; // Das löst eine Exception aus: 'Tabelle ist in Gebrauch' Result := DbiPackTable(Tbl.DBHandle, Tbl.Handle, nil, nil, True); end;
procedure TForm1.DatenTabBeforeScroll(DataSet: TDataSet); var i,i1:integer; begin for i:=0 to DatenTab.FieldCount-1 do if DatenTab.Fields[i].AsString<>'' then exit; DatenTab.Edit; DatenTab.Fields[0].AsString:='d'; DatenTab.Delete;
DatenTab.Active := false; DatenTab.Exclusive := true;
dgPackDbaseTable(DatenTab);
DatenTab.Active := false; DatenTab.Exclusive := false; DatenTab.Active := true; end; |
Nebenbei: warum wird in dgPackDbaseTable eine Exception ausgelöst obwohl ich den Table vorher deaktiviert habe? Ich bin am Verzweifeln - Bitte um Hilfe!
|
|
hwb
      
Beiträge: 91
Win98 SE, Win2000
D5 Prof
|
Verfasst: Di 24.06.03 22:56
Auch wenn die Tabelle deaktiviert ist, die Pack-Funktion öffnet sie wieder,
deshalb hab ich in meiner Demo aufs öffnen verzichtet.
Die Exception kommt beim öffner der Tabelle, das lässt darauf schliessen,
daß die Tabelle noch von anderer Seite geöffnet ist, evtl in der IDE. Damit ist ein öffnern im EXCLUSIV - Modus nicht möglich. In deiner Entwicklungsumgebung darf die Tabelle auf keinen Fall schon auf
active := true im Objektinspektor gesetzt sein. Damit ist die Tabelle bereits schon einmal geöffnet und kann nicht mehr ein weiteres mal im Exclusivmodus geöffnet werden. In meinem Muster öffne und schließe ich die Tabelle auch erst während das Programm läuft.
Und noch was: Das Packen der Tabelle würde ich bestimmt nicht in so eine Löschschleife wie du schreiben sondern evtl einen Menüpunkt Wartung oder so einbauen, unter dem man auch ein Reindex einbringen kann. Ausserdem: Öffne die Tabelle im Exclusivmodus schon vor der Packfunktion. Dann läßt sich auch ein Error besser abfangen - und den hast du schon, wenn dein Programm zweimal gestartet werden kann.
Gruß
HWB
|
|
dreamtheater 
      
Beiträge: 46
|
Verfasst: Mi 25.06.03 17:54
OK, das mit der Exception ließ sich lösen. Aber es hat natürlich einen Grund, daß ich den Restructure in die BeforeScroll-Routine nach dem Löschbefehl einfüge, denn ich will auf keinen Fall leere Datensätze in der Tabelle haben, und daher möchte ich, daß sofort überprüft wird, ob ein Datensatz leer ist, wenn dem so ist, daß er gelöscht wird, und ein neu hinzugefügter Datensatz den Index RecNo+1 kriegt. Ich möchte nämlich die Datenätze per Random Funktion ansprechen können (Grammatiktrainer). Und das ist natürlich umständlich zu bewerkstelligen, wenn ich nicht einfach random(DatenTab.RecNo)+1 nehmen kann.
Dumme Frage: Würde meine Routine so, wie ich sie geschrieben habe, theoretisch überhaupt funktionieren?
Kann es sein, daß das Restrukturieren nicht funktioniert, weil ich die Datenbank mit Excel erzeugt habe?
P.S.: Der Hauptgrund, warum ich mit einer Datenbank arbeiten will, ist, daß man die Datensätze nicht nur aus dem Programm heraus, sondern evtl auch im Excel oä bearbeiten können soll. Sonst würde ich natürlich ein einfaches File of string nehmen. Daß ich eine DB4 Datenbank verwende ist also mehr oder weniger willkürlich - es könnte jede andere sein, die mit Excel erstellt/bearbeitet werden kann; wäre hier vielleicht ein anderes als das DB4 Format besser geeignet ?
|
|
hwb
      
Beiträge: 91
Win98 SE, Win2000
D5 Prof
|
Verfasst: Mi 25.06.03 21:17
Also grundsätzlich sollte deine Funktion eigentlich schon gehen. das Problem ist ja eigentlich, daß das Packen eigentlich eine Servicefunktion ist. Intern werden deine Daten in eine neue Tabelle kopiert, dann die alte Tabelle gelöscht und Die neue Tabelle bekommt den alten Namen. Deshalb ist für einen Moment gar keine Tabelle vorhanden. Deshalb EXCLUSIVE . Und das hast du soviel ich weiß bei jeder nativen Datenbank. Der Tabelle ist es egal mit was du sie erzeugt hast. Nur wenn du zwischendurch mit einem anderen Programm arbeitest, dürfte der Index nicht mehr OK sein und muß neu erzeugt werden.
Ich würde das Problem von einer ganz anderen Seite mal überlegen:
Warum löscht du die Datensätze? Statt zu löschen kannst du die Sätze auch mit einem bestimmten Wert ausfüllen und dann mit einem Filter arbeiten. Wenn du einen Neuen Satz anlegst nimmst du einen von den ausgefilterten - wenn einer da ist - und editierst diesen nur. Dann kannst du halt nicht mit RECNO arbeiten sondern mußt eine interne Nummer vergeben. Das Ding soll doch sicher eine Einzelplatzversion werden. Auf der eigenen Festplatte sind Filter gans schön flott und ich gehe nu auch mal nicht von großen Datenmengen aus - < 100000 oder?
Gruß
HWB
|
|
dreamtheater 
      
Beiträge: 46
|
Verfasst: Sa 28.06.03 14:46
Danke! Du hast mich auf eine Gute Idee gebracht mit: "Warum löschst Du die Datensätze?" Habe das Problem jetzt so gelöst, daß ich immer einen leeren Datensatz am Ende der Tabelle habe, was leicht zu berücksichtigen ist, wenn ich die Datensätze per random anprechen will. Auch das Löschen von Datensätzen 'in der Mitte' der Datensätze haut jetzt mit 'richtiger' (d.h. lückenloser aufsteigender Reihenfolge) Numerierung hin (das mit den Filtern war nicht nötig, das Problem lag hauptsächlich bei einem leeren Datensatz am Schluß)
P.S.: Ja, klar die Anzahl der Datensätze wird 200 voraussichtlich nie übersteigen - so unregelmäßig ist die Spanische Sprache hoffentlich nicht ;o) - Löschen und Packen dürfte also kein Problem darstellen....
|
|
|