Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Frage zu tableupdate()

7 views
Skip to first unread message

tom knauf

unread,
Dec 10, 2009, 5:28:26 AM12/10/09
to
Moin ,moin

ich bearbeite hier einen geerbten �lteren Code und habe da ein
Verst�ndnisproblem :

Zun�chst wird eine Remoteview zum SQL-Server mit Use ge�ffnet.
Dann folgen einige Append blanks, repl ... with ... und zum Schluss NACH
alle Appends : Tableupdate(.t.)

also
use RV_xxx

nKey=neuerkey()
sele rv_xxx
appe blank
repl pk_xxx with nKey
repl nPos with 1

nKey=neuerkey()
sele rv_xxx
appe blank
repl pk_xxx with nKey
repl nPos with 2

nKey=neuerkey()
sele rv_xxx
appe blank
repl pk_xxx with nKey
repl nPos with 3

FLUSH
IF CURSORGETPROP("Buffering")>1
=TABLEUPDATE(.t.)
ENDIF

Nun passiert es laut User manchmal, da nur der letzte von mehreren S�tzen
(npos=3) in der Datenbank erscheint.

Frage
Was bewirkt der Tableupdate(.t.), wie sollte er "korrekt" lauten ?
(�nderungen anderer User an den S�tzen sind nicht m�glich, sind alles
appends)
w�re =Tableupdate(1,.t.,"rv_xxx") besser ?

Eigentlich sollten doch die Appends ein Tableupdate() auf dem SQL-Server
implizit ausf�hren,
da ja der Recordpointer ja wechselt (wie beim Skip) ?


Vielen Dank f�r Eure Hilfe

Verwirrte Gr��e aus Hamburg
Tom


Hans-Peter Grözinger

unread,
Dec 10, 2009, 5:53:56 AM12/10/09
to
Hallo Tom !

> IF CURSORGETPROP("Buffering")>1
> =TABLEUPDATE(.t.)
> ENDIF
>
> Nun passiert es laut User manchmal, da nur der letzte von mehreren
> S�tzen (npos=3) in der Datenbank erscheint.
>
> Frage
> Was bewirkt der Tableupdate(.t.), wie sollte er "korrekt" lauten ?

Je nach Puffermodus wird nur der letzte Datensatz zur�ckgeschreiben.
Ich w�rde das mal mit Tableupdate( 1, .T. ) probieren.
Damit werden _alle_ ge�nderten Datens�tze gespeichert.

> (�nderungen anderer User an den S�tzen sind nicht m�glich, sind alles
> appends) w�re =Tableupdate(1,.t.,"rv_xxx") besser ?

Ja, siehe oben.

> Eigentlich sollten doch die Appends ein Tableupdate() auf dem
> SQL-Server implizit ausf�hren, da ja der Recordpointer ja wechselt
> (wie beim Skip) ?

Nein, das passiert nur wenn man direkt auf Tabellen arbeitet aber
nicht bei Views und Remote-Views.

--
Hans-Peter Gr�zinger
TOFU ist gedankenlose Resourcenverschwendung.
http://einklich.net/usenet/zitier.htm
http://support.microsoft.com/default.aspx?scid=fh;DE;NGNetikette

tom knauf

unread,
Dec 10, 2009, 6:13:17 AM12/10/09
to
Hallo Peter,


danke sch�n f�r deine Info.

aus der Hilfe :
>3 = Optimistic record locks which wait until pointer moves, and then lock
>and update. The default value for Buffering is 1 for tables and 3 for
>views. If you use buffering to access remote >data, the Buffering property
>is either 3, optimistic row buffering, or 5, optimistic table buffering.

Dataenvironment oder verscheidene Session benutzen wir nicht, und ein
sqlsetprop() auch nicht.
Bei RV�s ist Buffering im Programm immer 3 (Row Buffering) , daher dachte
ich, dass beim Wechsel eines Records auch ein Update an den SQL-Server
ausgel�st wird.

Ich �ndere das Tableupdate in (1,.t.,"rv_xxx"), dann sind wir auf der
sicheren Seiten

Nochmals Danke
Tom


"Hans-Peter Gr�zinger" <hansp...@gmx.de> schrieb im Newsbeitrag
news:eWXYNcYe...@TK2MSFTNGP05.phx.gbl...

Stefan Wuebbe

unread,
Dec 10, 2009, 6:18:56 AM12/10/09
to
Hallo Tom -

> Frage
> Was bewirkt der Tableupdate(.t.), wie sollte er "korrekt" lauten ?
> (�nderungen anderer User an den S�tzen sind nicht m�glich, sind alles
> appends)
> w�re =Tableupdate(1,.t.,"rv_xxx") besser ?
>
> Eigentlich sollten doch die Appends ein Tableupdate() auf dem SQL-Server
> implizit ausf�hren,
> da ja der Recordpointer ja wechselt (wie beim Skip) ?
>

Genau. Wenn die Ansicht im BufferMode 3 (default) bleibt, w�rde
jede Datensatzzeiger-Bewegung ein implizites TableUpdate()
bewirken.

Aber TableUpdate() ist immer nur ein "Versuch", der fehlschlagen
kann. Also will man eigentlich immer "wissen", ob's geklappt hat,
also nie ein implizites TableUpdate() und auch nie ein explizites,
bei dem der Erfolg nicht gepr�ft wird.

Die L�sung k�nnte also in deinem Fall sein, das Ergebnis nach
jeder Append Blank / Replace Kombination (die auch auf eine
"Insert ... Into " Zeile reduziert werden k�nnte) zu pr�fen.
Und wenn .F., per AError() den Grund festzustellen.
Und ggfs. per TableRevert() die gepufferten "ung�ltigen"
Inhalte vor dem evtl. n�chsten Versuch zur�ck zu setzen.

Oder du benutzt BufferMode 5, und reduzierst auf einen
TableUpdate() Versuch am Ende der Prozedur (oder zB. in
100er/1000er Bl�cken, wenn die Zeilenzahl sehr gro� ist).

> w�re =Tableupdate(1,.t.,"rv_xxx") besser ?

Genau, bei Zeilenpufferung ist 1 als erster Parameter
sinnvoller als das zitierte Original.

Der zweite Parameter ist optional, je nach Konzept.

Den dritten Parameter zu benutzen ist immer gut (weil
so die Zeile in sich selbst abgesichert ist, und nichts
"dazwischen funken" kann, wie z.B. ein Grid als
ActiveForm.ActiveControl, das versucht grid.RecordSource
zum aktuellen Arbeitsbereich zu machen).

hth
-Stefan

tom knauf

unread,
Dec 10, 2009, 6:31:35 AM12/10/09
to
Hallo Stefan,

danke f�r deine Infos, nun bin ich wieder verwirrt.

Gen�gt bei Buffering 3 EIN Tableupdate(1,.t.,"rvxxx") am Ende nach den
Appends
oder MUSS ich nach JEDEM Append ein Tableupdate() ausf�hren,
bzw. Buffering auf 5 setzen ?

Danke im voraus
Tom

"Stefan Wuebbe" <stefan...@gmx.de> schrieb im Newsbeitrag
news:ut4tMqYe...@TK2MSFTNGP05.phx.gbl...

Stefan Wuebbe

unread,
Dec 10, 2009, 6:56:12 AM12/10/09
to
tom knauf wrote:
> Hallo Stefan,
>
> danke f�r deine Infos, nun bin ich wieder verwirrt.
>
> Gen�gt bei Buffering 3 EIN Tableupdate(1,.t.,"rvxxx") am Ende nach den
> Appends
> oder MUSS ich nach JEDEM Append ein Tableupdate() ausf�hren,
> bzw. Buffering auf 5 setzen ?

Tschuldige. Ich wollte sagen, bei Buffering 3 ist ein
expliziter TableUpdate() Versuch mit Ergebnispr�fung vor
jeder weiteren Datensatzzeiger-Bewegung gut.

Bei Buffering 5 k�nnte man zu Gunsten der Performance
darauf verzichten, und nur einmal am Ende ein TableUpdate()
versuchen.
(Oder, wenn es sich um *sehr viele Zeilen handelt, in
Bl�cken.)

Gru�
-Stefan

tom knauf

unread,
Dec 10, 2009, 7:10:07 AM12/10/09
to
Hallo Stefan,

danke f�r deine M�he, nun stimmt mein Buffering-Weltbild wieder.
Ich werde zur Sicherheit nach jedem Append und vor dem n�chsten neuerkey()

Ein Tableupdate(1,.t.,rv_xxx) mit Auswertung des R�ckgabewerts einbauen,
Dann ist implizit oder nicht egal.

Nochmals Danke

Gr��e aus Hamburg bei wundervollem Nieselregen
Tom

"Stefan Wuebbe" <stefan...@gmx.de> schrieb im Newsbeitrag

news:OwGrB$YeKHA...@TK2MSFTNGP04.phx.gbl...

Olaf Doschke

unread,
Dec 10, 2009, 7:31:57 AM12/10/09
to
> Ich werde zur Sicherheit nach jedem Append und vor dem n�chsten neuerkey()
> Ein Tableupdate(1,.t.,rv_xxx) mit Auswertung des R�ckgabewerts einbauen,

Das ist nicht der interessierende Moment.

Ein APPEND BLANK in Deinem Code l�st das Speichern des vorhergehenden Satzes
aus, wenn Du also interesse daran hast, ob dieser Insert klappt, machst Du
besser ein Tableupdate() vor dem APPEND. Nur das erste Append l�st kein
Insert aus, weil es ja erst der erste Insert ist. Direkt nach dem APPEND
stehst Du auf einem noch leeren und ungespeicherten neuen Datensatz, der
vorerst nur im View exisitert. Der Satz auf dem Du dann aktuell stehst ist
gebuffert, auch bei Zeilenweiser Bufferung. Erst durch den n�chsten APPEND
BLANK l�st Du das impliziete Tableupdate() des Datensatzes aus.

Insofern geh�rt ein Tableupdate() immer ans Ende eines Abschnitts aus APPEND
BLANK und REPLACE. APPEND und REPLACE kannst Du allerdings als INSERT-SQL
zusammenfassen.

Was noch st�rt und vermutlich die Ursache im Mehrbenutzerbetrieb f�r
Insert-Konflikte ist, ist Deine lokale Vergabe von Keys per Funktion
neuerkey(). Es h�ngt davon ab, ob die Funktion einen Wert immer nur einmal
rausgibt, ober z.B. MAX(id)+1 aus der Datenbank ermittelt. In erstem Fall
ist alles okay, im 2. Fall k�nnen aber 2 Clients, die gleichzeitig
neuerkey() nutzen gleiche keys erzeugen.

Tsch��, Olaf.

tom knauf

unread,
Dec 10, 2009, 7:49:40 AM12/10/09
to
Hallo Olaf,


danke f�r deine Mail, also gemeint habe ich es so :
use RV_xxx

nKey=neuerkey()
sele rv_xxx
appe blank
repl pk_xxx with nKey
repl nPos with 1

If !tableupdate(1,.t.,"rv_xxx")
do panic
endif

nKey=neuerkey()
sele rv_xxx
appe blank
repl pk_xxx with nKey
repl nPos with 2

If !tableupdate(1,.t.,"rv_xxx")
do panic
endif

nKey=neuerkey()
sele rv_xxx
appe blank
repl pk_xxx with nKey
repl nPos with 3

If !tableupdate(1,.t.,"rv_xxx")
do panic
endif

Ist das so korrekt ?

Interessant w�re noch, ob der Append blank nochmal Trafic zum SQL-Server
aufbaut, obwohl ja gerade tableupdate() lief.
Aber wenn das ein Problem wird, stele ich auf buffering=5 um.

neuerkey() arbeitet mit einer im Netz liegenden Foxprotabelle (nix SQL) mit
Recordlocking(),.. und ist SEHR stabil.
Autoinc mag ich aus verschiedenen Gr�nden weder bei Fox noch bei SQL und ich
habe noch keine Nachteile vom Fox gegen�ber z.B. einer SQL-Tabelle/Stored
Procedure finden k�nnen. Die Umstellung auf GUIDS ist mir zuviel Arbeit f�r
das alte Projekt.

Gr��e aus Hamburg
Tom


"Olaf Doschke" <b2xhZi5kb3NjaGt...@strconv.14.de> schrieb im
Newsbeitrag news:eVSrGTZe...@TK2MSFTNGP04.phx.gbl...

Olaf Doschke

unread,
Dec 10, 2009, 12:12:31 PM12/10/09
to
> Ist das so korrekt ?
Ja, so machst Du die Updates, wenn der jeweilige Datensatz fertig ist.

> Interessant w�re noch, ob der Append blank nochmal Trafic zum SQL-Server
> aufbaut, obwohl ja gerade tableupdate() lief.

Kann ich Dir nicht genau sage, ein impliziter Tableupdate() l�uft wohl immer
ausgel�st vom Datensatzwechsel, aber da dann nix mehr im Buffer ist d�rfte
zum SQL Server tats�chlich nix mehr laufen.

> neuerkey() arbeitet mit einer im Netz liegenden Foxprotabelle (nix SQL)
> mit Recordlocking(),.. und ist SEHR stabil.

Okay, gut, das liest sich so, da� Du auf jeden Fall einen Z�hler damit
pflegst und zwei Aufrufe dann auch aufeinanderfolgende Nummern liefern, dann
kann das wanderen der Werte in den SQL-Server auch durchaus warten.

> Autoinc mag ich aus verschiedenen Gr�nden weder bei Fox noch bei SQL und
> ich habe noch keine Nachteile vom Fox gegen�ber z.B. einer
> SQL-Tabelle/Stored Procedure finden k�nnen. Die Umstellung auf GUIDS ist
> mir zuviel Arbeit f�r das alte Projekt.

Ich kann mir gut vorstellen, warum die Umstellung auf IDENTITY(1,1) Integer
am SQL Server ein Problem aufwirft, n�mlich dann, wenn Du zun�chst mal neue
Daten in Views aufbaust, bevor Du sie dann wegspeicherst, und innerhalb der
Viewcursoren schon relationen zwischen Haupt- und Kinds�tzen gelten sollen.

Man k�nnte allerdings tats�chlich unbedenklich folgendes als neuerkey()
einsetzen, was auf Autoinc basiert - auch im Multiuserbetrieb. Mir hat
n�mlich noch niemand zugetragen, da� es Probleme mit Autoinc g�be, und auch
in eigener Erfahrung hatte ich damit nie Probleme.

? neuerkey() && 1
? neuerkey() && 2
? neuerkey() && 3

Function neuerkey()
Local lcAliasKeytable, liID

* Multilocks k�nnte und sollte generell schon ON sein, kann man sich dann
sparen.
Set Multilocks On

lcAliasKeytable = "C:\test\tabID.DBF"
* vielleicht eher
* lcAliasKeytable = goApp.oConfig.GetSetting("KeyTablePath")+"tabID.dbf"
* Pfadkonstante geht zur Not aber auch.

If NOT File(lcAliasKeytable)
Create Table (lcAliasKeytable) (iID I Autoinc Nextvalue 0 Step 1)
Append Blank && erste iID = 0, wird hier verbraten
Use in tabID
Endif
Select 0
Use (lcAliasKeytable) Shared
BLANK DEFAULT AUTOINC IN tabID && hochz�hlen
liID = iID
Use in tabID

Return liID
Function


Das trickreiche daran ist, da� Autoinc als Z�hler genutzt wird, die Tabelle
dabei aber immer nur einen Datensatz mit einer zuletzt vergebenen ID hat und
nicht st�ndig anw�chst (wegen BLANK statt APPEND). Man kann das erstellen
der Tabelle nat�rlich auch au�erhalb der Funktion manuell machen und so noch
die paar Zeilen sparen, aber man braucht sich um Locking keinen Kopf machen,
das macht der Autoinc-Mechanismus schon selbst sauber. Der einzige Punkt, an
dem die tabID.DBF und dessen Header kurz gesperrt sind ist die Zeile BLANK
DEFAULT AUTOINC IN tabID, k�rzer kann man die Sperre nicht halten.

Tsch��, Olaf.


tom knauf

unread,
Dec 11, 2009, 4:40:40 AM12/11/09
to
Hallo Olaf,

den Autoinc f�r eine Primarykeytabelle zu nutzen ist ein interessanter
Ansatz.

Vermieden habe ich Autoincs in den Tabellen (PK_ADR in ADR) selbst,
da es bei der Synchronisation von Firmenniederlassungen eher st�rt.

Bisher nutze ich eine Tabelle syskeys tablename C(20), iLastkey I
F�r jede Tabelle steht ein Ilastkey drin (Niederlassungen bekommen eine mit
Offset)
Brauche ich jetzt einen neuen PK f�r "ADR" zieht neuerkey("ADR") indem er in
syskey den Ilastkey f�r tablename="ADRESSEN" um einen erh�ht.
Hier k�nnte man den Autoinc verwenden, mal sehen, teste ich mal aus, spart
die ganze Mimik mit set reprocess, rlock,...

Danke sch�n

Gr��e
tom

"Olaf Doschke" <b2xhZi5kb3NjaGt...@strconv.14.de> schrieb im

Newsbeitrag news:%23Q404vb...@TK2MSFTNGP06.phx.gbl...

Olaf Doschke

unread,
Dec 13, 2009, 7:27:31 AM12/13/09
to
Hallo Tom,

die Problematik mit Readonly von Autoincfeldern l��t sich mit Set
AutoIncError etwas entsch�rfen, aber auch das unterdr�ckt nur den Fehler und
�bernimmt dann nicht den Wert von der Filiale.

GUIDs sind zur Replikation nat�rlich einfacher, langfristig sollte man sich
das immer als Ziel nehmen, wenn es Synchronisationen von Datenbest�nden
gibt. Ich habe dazu meine ich schonmal hier oder in den dfpug-NGs einen
l�ngeren Beitrag zur Migration von IDs zu GUIDs geschrieben.

Denkbar ist immer ein zus�tzliches GUID Feld, das bedeutet zun�chst mal
keinen �nderungsaufwand an der Applikation selbst, kann aber zur
Synchronisation als sozusagen hochwertigerer Primary Key genutzt werden.

> Hier k�nnte man den Autoinc verwenden, mal sehen, teste ich mal aus, spart
> die ganze Mimik mit set reprocess, rlock,...

Ja, insbesondere kannst Du auch den Offset einsetzen, bzw. jeweils mit der
vor Ort h�chsten ID initialisieren.

> Danke sch�n
gerne.

Tsch��, Olaf.

0 new messages