infobase: EDV - MS-Access


Variablen

Darstellung   Quelle: dmt   Datum: 10.2004   nach oben

Bei der Darstellung von Variablenwerten kommt es vereinzelt zuverzwickten Situationen:

Gerade bei Berechnungen mit Zeitwerten werden offensichtlich glatte Werte als xyz,999999999 u.ä. ausgegeben.
Da hilft dann schon mal eine Umwandlung von Single- und Double-Werten, die irgendwo im Milliardstelbereich leicht verrutscht sind, in ganzzahlige Strukturen (Integer oder Überlauf-sicherer Long), womit ein 0,99999999 zu einem aussagekräftigerem 1 wird.

Auch die Darstellung per fester Format-Zuweisung a'la Format$(v, "#,##0.##") ist auch nicht immer befriedigend, wenn z.B. Werte wie "1," ausgegeben werden.

Ein Beispiel für eine schnuckelige Lösung, die den Sehgewohnheiten des menschlichen Betrachters entgegenkommt, zeigt das folgende Code-Schnippsel.

Es stellt 1 als "1 h", 485,5 als "485,5 h" und 54,25 als "54,25 h" dar.

Dieses Verfahren zeigt Werte mit dynamischem Format, ist aber für eine Listendarstellung ungeeignet, da dort nur ein starres Format die Daten einheitlich darstellen kann.

v = DLookup("Summe_netto", "Leistungszeit_pro_Auftrag_Q", "Auftrag=" & Me!Nr)
If Not IsNull(v) Then
   v = CLng(v * 100)
   v = v / 100
   Me!Text_UF_Info.Caption = v & " h"
End If


deklarieren   Quelle: dmt   Datum: 03.2004   nach oben

DEKLARIEREN von Variablen:

Interessant sind Sonderfälle bei der Deklaration von Variablen, die natürlich nicht oder nur an möglichst unauffindbarer Stelle dokumentiert sind.

Dim Zeichenkette as String * 8 ' String mit festen 8 Zeichen Länge

Mit den Anweisungen LSet und RSet können mit derartig deklarierten String's auch formatierte Text-Ausgaben in Messageboxen und ähnlich unflexiblen Ausgabemedien erfolgen.


globale   Quelle: dmt   Datum: 03.2004   nach oben

GLOBALE VARIABLEN:

werden im Deklarationsteil eines Modules als GLOBAL deklariert. Sie gelten dann solange auch in allen Prozeduren aller Formulare, die danach geöffnet werden, bis zum Beenden der Applikation oder bis einem Laufzeitfehler mit einem Reset des Interpreters der Garaus gemacht wird. Variablen, die im Deklarationsteil eines Formulares mit DIM deklariert wurden, gelten innerhalb der Prozeduren des Formulares als 'global'.


Objekt-Variablen per Set zuweisen   Quelle: diverse   Datum: 08.2010   nach oben

Zuweisen einer Objekt-Variablen per Set-Anweisung

Wieder mal verwundert musste ich feststellen, daß auch beim Zuweisen von Objektvariablen per Set-Anweisung unerwartete Stolperfallen existieren.

Im Rahmen einer kleinen Anwendung wollte ich eine Prozedur-lokale Kopie eines Anwendungs-globalen Recordsets erstellen.

Damit wollte ich innerhalb der Recordset-Kopie navigieren können, ohne die aktuelle Zeigerposition des globalen Recordsets zu beeinflussen.

Prompt musste ich sehen, dass sich die Zeigerposition des globalen Recordsets sklavisch mitänderte.

Der Grund liegt schlicht und ergreifend daran, daß mind. bei Access 2 die Übergabe von Objektbezügen per Set-Anweisung per Referenz erfolgt und man deswegen letztendlich am Ur-Objekt arbeitet, ohne eine Kopie davon zu haben

So schreibt denn auch die Dokumentation:
.
Wenn Sie Set verwenden, um einer Variablen eine Objektreferenz zuzuordnen, wird für diese Variable keine Kopie des Objekts, sondern eine Referenz zu diesem Objekt erstellt. Mehrere Objektvariablen können auf ein- und dasselbe Objekt verweisen. Da es sich bei diesen Variablen um Referenzen zu diesem Objekt und nicht um Kopien des Objekts handelt, bewirkt eine Änderung des Objekts gleichzeitig die Änderung sämtlicher Variablen, die auf dieses Objekt Bezug nehmen.


Parameter   Quelle: dmt   Datum: 03.2004   nach oben

VARIABLEN ÜBERGEBEN / per REFERENZ oder BYVAL / PARAMETER:

Eine Sache, die ich auch erst per Zufall entdeckt mußte, obwohl sie eigentlich zum Grundwissen eines Programmierers gehört:

Bei der Entwicklung der dress-Treppen-Optimierung mußte festgestellt werden, daß sich Variablenwerte von Geisterhand zu verändern schienen. Nach angestrengtestem Suchen fand ich heraus, daß das immer mit solchen Variablen passierte, die an andere Routinen übergeben und denen dort (auch wenn sie dort unter einem anderen Namen geführt wurden), Werte zugewiesen wurden.

Die Ursache hierfür liegt in dem Umstand, daß Access-Basic wie auch Visual-Basic Werte als Referenzen übergibt. Das bedeutet, daß nicht der Variablenwert an sich, sondern immer die Adresse der Variable, in der Wert steht, übergeben wird. Wird diese Variable in einer anderen Routine entgegengenommen (egal unter welchem Namen), wird letztendlich nur wieder auf diese Adresse gezeigt. Wird (egal in welcher Routine) einer Variablen, die den Wert dieser Adresse enthält, ein neuer Wert zugewiesen, ändert sich eben der Adresseninhalt. Beim Verlassen dieser Routinen (auch bei Sub's, die selbst ja gar keinen Wert zurückgeben), kann jedesmal, wenn die UrVariable erneut benutzt wird, festgestellt werden, daß diese einen neuen Wert enthält. ByVal ändert dieses Verfahren und übergibt lediglich eine 'vorübergehende' Adresse.

Access hält dazu folgende Informationen bereit, die übrigens zu 99% mit der englischen Visual-Basic 3.0-Hilfe übereinstimmen:

    Sie können die Argumente für eine Prozedur in Form von Referenzen
    oder als Werte eingeben.

    Argumentenwerte, die als Referenzen übergeben werden, können von der
    Prozedur beim Ausgeben der Argumente geändert werden; Access Basic
    übergibt die aktuelle Adresse des Arguments. Den Argumenten, die als Werte
    übergeben werden, werden vorübergehende Adressen zugewiesen. Die Werte
    können nicht geändert werden.

    Argumente werden standardmäßig als Referenzen übergeben.

    Dies trifft nur dann nicht zu, wenn sie in Klammern oder unter Verwendung
    des reservierten Wortes ByVal deklariert werden.

Merke:

Variablen werden IMMER als Referenzen übergeben. Soll damit temporär gearbeitet werden, sollte das sicherheitshalber mit in den einzelnen Routinen extra deklarierten Variablen geschehen. Das führt über kurz oder lang zu einem schulbubenmäßigen FailSafe-Programmierstil a'la OA3.

Bleibt die Frage, wozu Funktionen in Basic eigentlich noch gut sind. In den bekannten Dokumentationen wird ihr Vorteil darin gesehen, daß sie EINEN Wert zurückgeben können.

Die standardmäßige Referenziererei erlaubt aber, soviele Parameter geändert zurückzugeben, wie eben übergeben wurden, unabhängig von Function oder Sub. Sogar selbstdefinierte Datensatz-ähnliche Typen in Form von Parametern können übergeben und in anderen Routinen verwurstet werden, so daß der Rückgabewert einer aufgerufenen Funktion bestenfalls in einem Fehler-indizierenden Rückgabe-Integer besteht, der selbst aber auch als Parameter an eine Rückgabe-lose Sub-Routine funktionieren würde.

Wahrscheinlich stammen sie aus den Untiefen 'systemnaher' Programmiersprachen und sind in Access immerhin noch dafür dar, um aus Makros (würg!) oder den pseudodeutschen Entwurfsmodus-Eigenschaften-Fenstern oder mit dem völlig beschissenen Ausdrucks-Editor (dann doch lieber EDLIN oder gleich der Sprung aus dem Fenster) heraus angesprochen werden zu können.

Vielleicht treffe ich eines Tages einen Menschen, der mir hierzu irgendetwas sinnvolles sagen kann.

Das scheinbar aus der Steinzeit stammende Programmierkonzept von OA3, das weder Sub's noch Funktionen, geschweige denn zu übergebende Parameter kennt (im nachhinein scheint es unmöglich, daß damit überhaupt Anwendungen entwickelt werden konnten), ist wie in so vielen Punkten wesentlich aufgeräumter und für den Menschen (wer sonst arbeitet denn eigentlich mit Computern?) besser nachvollziehbar, weil logischer.

Im Entwicklungs-Laufzeitmodus ging fast alles, der Compiler war etwas strenger, Variablen waren grundsätzlich lokal, globale Variablen konnten nach Belieben in allen Routinen deklariert und entfernt werden, und standen damit all den Routinen zur Verfügung, die nachfolgend aufgerufen wurden. Da diese globalen Variablen immer mit ihrem Namen angesprochen wurden, war für den Programmierer eigentlich immer klar, was er denn überhaupt tut.

Doch damit nicht genug:

Der öfter auftretende Fehler, daß der Zeichenketten-Inhalt 'sau' eines Formularfeldes nicht als Parameter an eine String-Variable einer Funktion übergeben werden konnte, sondern zuerst einer eigens deklarierten String-Variablen zugewiesen werden mußte ('Ungültiger Parameter-typ') konnte mit ByVal behoben werden.


Probleme   Quelle: dmt   Datum: 03.2004   nach oben

Der Wahnsinn hört nicht auf oder auch Neues aus Akte X:

Am 12.02.97 gelang im Hause DMT die Entdeckung eines weiteren, bisher unbekannten Datentypes:

Die Funktion

    Function Get_Startort (vVon As Variant, sGrund As String, vbei As Variant, iFormTermine As Integer) As String

wird u.a. von einem Formular Termine aus aufgerufen. Da für den Parameter vbei gelegentlich der Umstand eintrat, daß vom Formular ein Nullwert übermittelt wurde und somit jegliche Stringvergleiche in der Funktion in die Hose gingen, wurde dieser kurzerhand als Variant deklariert. Für Datensätze, deren Grund Null war, gab die Funktion jedoch jedesmal '#Fehler' zurück, so daß ich auch den Parameter sGrund als Variant deklarieren wollte. Doch dann hängt sich die Anwendung jedesmal auf, wenn für diesen Parameter Nullwerte auftreten (soll das heißen, daß eine Funktion nicht mehr als einen Variant-Parameter entgegennehmen kann ? ). Also, so wollte es halt nicht gehen. Doch der Hammer kommt erst noch: als ich versuchte, evtl. Nullwerte für diesen Stringparameter in der Funktion abzufragen, mußte ich feststellen, daß sich die Variable in diesen Fällen ÜBERHAUPT nicht packen ließ. Nach hilflosem Einblenden irgendwelcher MsgBox'en merkte ich, daß die Funktion bei diesen Fällen erst gar nicht durchlaufen wird. Beide Formularfelder, die als Variant Linderung zu versprechen schienen, sind Kombinationsfelder. Warum bei einem die Sache klappt und beim anderen nicht, bleibt ein Rätsel, das Bill Gates wohl mit ins Grab nehmen wird.


Vergleich, binärer   Quelle: dmt   Datum: 03.2004   nach oben

BINÄRER VERGLEICH:

Die schon öfter aufgetauchte Frage, ob Access Strings auch binär bzw. case-sensitiv vergleichen kann, kann mit ja beantwortet werden. Die Anweisung Option Compare {Binary | Database | Text} im Deklarationsteil eines Moduls regelt das für jedes Modul einzeln. Ein Weglassen der Anweisung stellt automatisch das scharfe Binary ein.


Vergleich, bitweiser   Quelle: dmt   Datum: 03.2004   nach oben

Eine sehr obskure Sache sind BIT-weise Zeichenvergleiche mittels AND / BITMASKEN:

Wie man am obigen Beispiel sehen kann, wird man in den Niederungen mancher Program-miersprachen mit seltsam anmutenden Konstrukten konfrontiert. Auf der einen Seite ist es toll, daß numerische Werte als Bit-Muster interpretiert entsprechend verglichen werden können, aber wenn man damit nicht vertraut ist und die Sache nirgendwo erklärt wird, ist der Nutzen, der aus solchen Features gezogen werden kann, doch wohl sehr gering.

Als echter Hochsprachenprogrammierer (Programmiersprachen sollten ein Werkzeug sein, um Probleme lösen zu helfen und nicht, um neue zu schaffen, wo vorher keine waren) kann ich mich nur wundern, aber nach langwierigem Ausprobieren scheint sich eine Theorie zur Funktionsweise des bit-weisen Vergleich zu bilden.

Der bit-weise Vergleich scheint sich nach folgendem Muster abzuspielen:

Gegeben sei ein ganzzahliger Wert x und ein yFlag, das einer 2er-Potenz entspricht.

Die Anweisung mit einer einfachen 'and'-Kombination

    If x and yFlag then

prüft, ob der einer 2er-Potenz entsprechenden Wert von yFlag in x enthalten ist.

So kann mit Hilfe solcher Anweisungen überprüft werden, welche 2er-Potenzen in 54 enthalten sind: 2, 4, 16 und 32, aber nicht 8 !

Leider schlugen alle Versuche, eine solche Anweisung zu erweitern, fehl.
Eine Prüfung a'la 'ist in 54 16 und auch 32 enthalten ?' muß nach derzeitigem Wissenstand durch lauter Einzelprüfungen ersetzt werden.

Etwas klarer scheint diese Angelegenheit bei einem bitweisen Vergleich per OR zu sein:

    Const SHAREAWARE = &H4000                              ' 16384
    Const PATHMUSTEXIST = &H800                            '  2048
    Const HIDEREADONLY = &H4                               '     4

    Flags = SHAREAWARE Or PATHMUSTEXIST Or HIDEREADONLY    ' 18436

In diesem Fall liegt per OR eine reine Addition der Bit-Werte vor. Wie das allerdings innerhalb der API-Funktionen auseinandergewurschtelt wird ( 2048 ist z.B. ein Vielfaches von 4), bleibt im Dunkeln. Die Dokumentation hierzu ist wie üblich dürftig.

Auch wenn AND nicht zu bitweisen, sondern nur zu 'normalen' logischen Vergleichen herangezogen wird, kommt nicht unbedingt Freude auf:

So scheint bei einer Anweisung a'la

    if ( wahrerAusdruck1 AND wahrerAusdruck2) then
        . . .
    end if

der implizite Vergleich von if zu mißlingen.

Erst ein deppenmäßiges

    if ( ( wahrerAusdruck1 = true ) AND ( wahrerAusdruck2 = true ) ) = true then
        . . .
    end if

scheint die Wahrheit des Vergleiches, ob beide Ausdrücke wahr sind, zu bestätigen.

Weitere Informationen zu diesem Thema konnten wider Erwarten in der Access-Hilfe unter MouseDown/Up-Ereignisse gefunden werden. Hier werden der Ereignisprozedur die Parameter Button, Shift, X und Y mit auf den steinigen Weg gegeben. Hinter Button und Shift können sich diverse Maustasten sowie Tastaturzustände verbergen. Der jeweilige Sachverhalt kann/muß im Code mit bitweisen Vergleichen gecheckt werden.

Dazu jetzt ein Beispiel aus der Access-Doku:

Sub Text1_KeyDown (KeyCode As Integer, Shift As Integer)
    Const KEY_F2 = &H71    ' Konstanten definieren.
    Const SHIFT_MASK = 1
    Const CTRL_MASK = 2
    Const ALT_MASK = 4
    Dim UmschaltGedrückt, AltGedrückt, StrgGedrückt, GedrTaste
    UmschaltGedrückt = (Shift And SHIFT_MASK) > 0
    AltGedrückt = (Shift And ALT_MASK) > 0
    StrgGedrückt = (Shift And CTRL_MASK) > 0
    If KeyCode = KEY_F2 Then    ' Tastenkombin. anzeigen.
       ' Zunächst wird ermittelt, welche Tasten gedrückt wurden;
       ' anschließend wird die anzuzeigende Meldung einer Variablen
       ' zugewiesen.
    If UmschaltGedrückt And StrgGedrückt And AltGedrückt Then
        GedrTaste = "Umschalttaste + Strg + Alt + F2."
    ElseIf UmschaltGedrückt And AltGedrückt Then
        GedrTaste = "Umschalttaste + Alt + F2."
    ElseIf UmschaltGedrückt And StrgGedrückt Then
        GedrTaste = "Umschalttaste + Strg + F2."
    ElseIf StrgGedrückt And AltGedrückt Then
        GedrTaste = "Strg + Alt + F2."
    ElseIf UmschaltGedrückt Then
        GedrTaste = "Umschalttaste + F2."
    ElseIf StrgGedrückt Then
        GedrTaste = "Strg + F2."
    ElseIf AltGedrückt Then
        GedrTaste = "Alt + F2."
    ElseIf Shift = 0 Then
        GedrTaste = "F2."
    End If

    ' Die Meldung in einem Textfeld anzeigen.
    Forms![Formular1]!Textfeld1.Text = "Sie drückten " & GedrTaste
    End If
End Sub

Da ist aber immer noch viel im Dunkeln:

x = (1 OR 2)        weist den Wert 3 zu, der Gegenvergleich x = (1 OR 2) fällt -1 (True) aus.
x = 1 OR 2 OR 3     weist 3 zu (den Wert des letzten Wertes), unergiebig
x = (1 OR 2 OR 4)   ergibt 7

Das kann wie folgt abgefragt werden:

1 = (x AND 1)        das geht dann munter so weiter bis 7; erst
0 = (x AND 8)        hört mit dem Spielchen auf.
7 = (x AND -1)       ist auch witzig und geht mit
3 = (x AND -5)       bis 1 = (x AND -7) weiter

Ok, für sowas ist mir meine Zeit mittlerweile zu schade. Man kann zum recht anschaulichen Beispiel zumindest soviel sagen:

Der übergebene Shift-Wert setzt sich aus per OR-Verknüpfung gebildeten Werten zusammen und kann bei den Einzelwerten 1 Shift, 2 Ctrl und 4 Alt die Werte 0, 1, 2, 3, 4, 5, 6 und 7 annehmen. In dem Beispiel werden für die zugrundeliegenden, wertbildenden Elemente Variablen vereinbart, die jeweils einzeln per

StrgGedrückt = (Shift And CTRL_MASK) > 0

geprüft. Ist Shift = 5, wurde es aus den Werten 1 und 5 gebildet. Die obige Prüfung fällt mit 0>0 falsch (0) aus.

Die etwas lockere, anschließende Prüfung mit

ElseIf UmschaltGedrückt And StrgGedrückt Then

prüft nur, ob

UmschaltGedrückt <> 0
und ob
StrgGedrückt <> 0
ist. Wurde z.B.
Shift = 3 übergeben, nimmt
StrgGedrückt = (Shift And CTRL_MASK) > 0
den Wert
2 = CTRL_MASK
an.

Ich hätte wahrscheinlich eher ein if

(Shift And CTRL_MASK) > 0 then StrgGedrückt = True
eingesetzt und das später auch dementsprechend abgefragt.


Vergleich, gerade, ungerade   Quelle: dmt   Datum: 03.2004   nach oben

Ist eine Zahl GERADE oder UNGERADE ?

Function IsEven (i As Integer) As Integer

    If i Mod 2 = 0 Then
       IsEven = True
    End If

End Function


Vergleich, Probleme   Quelle: dmt   Datum: 03.2004   nach oben

LOGISCHE VERGLEICHE:

Ein eher klassischer Fehler auf Seiten Microsoft's ist die Aussage der Dokumentation, daß die Funktion 'SysCmd(SYSCMD_RUNTIME)' den Wert 'True' zurückgibt, wenn der Code innerhalb der Laufzeitversion von MS-Access ausgeführt wird. Eigentlich logisch, daß das in der Praxis einfach nur NICHT funktioniert, denn diese Funktion gibt keinesweg den Wert 'True' bzw. '-1', sondern vielmehr denn Wert '1' zurück, der zwar sehr wohl einer einfachen if-Bedingung

                'If SysCmd(SYSCMD_RUNTIME) Then'

standhält, aber bei dokumentations-gemäßem
                'If SysCmd(SYSCMD_RUNTIME) = True Then

halt nicht zum Ziel führt.

Oder auf deutsch: es wird zwar ein wahrer Wert (verschieden von 0) zurückgegeben, aber keineswegs ein Wert, der gleich WAHR ist !

Das geht sogar soweit, daß der negative Aufruf 'If Not SysCmd(SYSCMD_RUNTIME)', mißlingt, ebenso ein äußerst explicites 'If Not SysCmd(SYSCMD_RUNTIME) = True'. Nur der Vergleich mit einem expliziten False erkennt den negativen 'nicht-runtime'-Fall:

    If SysCmd(SYSCMD_RUNTIME) = False Then
       DoCmd MoveSize 15, 15, 5300, 6315
    End If

MERKE:

Das Prüfen auf Ablauf innerhalb der Runtime-Umgebung erfolgt positiv mit

    If SysCmd(SYSCMD_RUNTIME) Then

oder negativ mit

    If SysCmd(SYSCMD_RUNTIME) = False Then

und nichts anderes.


Aber auch so einfache Sachen wie logische Vergleiche per If oder Select Case gelingen nicht immer:

Ein Case InStr(v, "Set_Click_Property") > 0 tut so, als ob nichts gewesen wäre.
Der gleiche Ausdruck per if führt dann sehr wohl zum gewünschtem Ergebnis.
Man muß eben IMMER alles ausproBIERen:


Vergleich, Variant   Quelle: dmt   Datum: 05.2006   nach oben

Aber es geht noch schlimmer, oder 'wie sehr kann ein VARIANT von sich selbst variieren ?'

Tja, Variant-Variablen sind eben auch nicht mehr das, was sie einmal waren. So mußte beim Vergleich von Steuerelement.OldValue mit dem aktuellen Value festgestellt werden, daß NULL nicht unbedingt gleich mit NULL ist.

Dazu fehlen selbst mir die Worte, aber ich habe weitgehend kommentarlos eine Funktion geschrieben, die das erledigt (hochnotpeinlich!!!).

Function Variant_Verschieden (v1 As Variant, v2 As Variant) As Integer

    If (v1 <> v2) Or (IsNull(v1) And Not IsNull(v2)) Or (Not IsNull(v1) And IsNull(v2)) Then
       Variant_Verschieden = True
    End If

End Function

****

Im Jahre 2006 mußte ich doch allen Ernstes weitere, neue Bekanntschaften mit aus meiner Sicht eigenartigen Verhaltensweise von Variant-Variablen machen.

Zur Verdeutlichung gegeben sei eine kleine Drecksfunktion:

Function test () As Variant

   Dim v As Variant

   test = v

End Function

Was glaubt der geneigte Leser wohl, welchen Wert diese Funktion wohl zurückliefert ?

Ich hätte auf NULL gewettet - und die Wette haushoch verloren.

Die Access-2.0-Hilfe klärt mich im Eintrag über die Funktion VarType() darüber auf, daß eine Variable, die ich sehr wohl deklariert habe, den VarType 0 besitzt und als "leer" anzusehen ist, weil sie noch nicht "initialisiert" wurde.

Ok, die Pille schlucke ich, denn NULL ist ein besonderer Zustand (VarType 1), der bedeutet, daß eine deklarierte und initialisierte Variable vom Typ Variant eben "keine gültigen Daten" enthält.

Aber woher bitte schön kommt dann der fragwürdig beschissene Zustand her, der mir den Rückgabewert dieser Drecksfunktion doch glatt als "" liefert. Dagegen hätte ich jederzeit höchste Beträge gewettet, doch der Vergleich test() = "" fällt WAHR aus !

Das bedeutet aber, das die nicht initialisierte Variable v nicht nur vom VarType 0, sondern auch vom VarType 8 (Zeichenkette ohne Inhalt) ist.

Hat da jemand in den frühen Achtzigern schlechtes Gras geraucht ???


Vergleich, Zahlen   Quelle: dmt   Datum: 03.2004   nach oben

INT oder wie werden Zahlen so verbogen, damit endlich ein falsches Ergebnis herauskommt.

Ein nicht aufhörendes Problem ist das sich Verrechnen einer Routine im Strobel-Manager, die eingegebene Zeitwerte addiert und in einer besonderen Form darstellt (z.B. '3.288:45'). Hierzu werden die Anzahl enthaltener Tage ermittelt sowie die Anzahl verbleibender Stunden. Von diesen Stunden wird der ganzzahlige Anteil übernommen und dazu wird Tage*24 addiert, um einen Ausdruck wie '3.288 h' realisieren zu können. Der auszugebende Wert setzt sich aus zwei Format$-Ausdrücken zusammen, in dem ein ':xx' als Minuten angehängt wird.

Eine tolle Sache, wenn nicht ständig Rundungsfehler auftreten würden. So werden z.B. 2 Einträge a'la 05:30 und 01:30 gnadenlos zu 06:00 zusammengezählt. Selbst ein Nachvollziehen mit CVDate und Cdbl ergibt den Wert '7'. Ein Vergleich mit diesem Ausdruck und '=7' wird jedoch negativ beschieden !

Scheinbar ergibt sich aus der Addition solcher Zahlen ein Wert, der so dicht an 7 liegt, daß er auch als simple '7' angezeigt wird, aber mit 7 eben nicht gleich ist.

Soll ich lachen, oder was ?

Ein CInt(Wert) ergibt aber das erwartete 7.

Im Strobel-Manager wurde das auf allerpeinlichste Weise gelöst:

Ist der übergebene Zeitwert

< 1
(max 23:59), wird er direkt im Format hh:nn ausgegeben.
Ist er größer, wird geprüft, ob nach Multiplikation mit 24 ein gerader Stundenwert herauskommt oder nicht. Gerade bei den geraden Werten wurde mit Int(gerader Wert) ein 'gerader Wert-1' ausgegeben. Deswegen erfolgt für diesen Fall eine simple String-Umwandlung mit Tausender-Formatierung und Ergänzung mit ':00'. Bei einer ungeraden Stundenanzahl werden die Ziffern vor dem Komma ausgelesen, tausendermäßig formatiert und mit ':' sowie dem Urwert im Format 'nn' ergänzt, aber das war noch nicht alles, deswegen nun etwas völlig anderes:

LECKEN !

Der Fehler tritt wahrscheinlich schon früher auf, wenn ein Zeitwert 07:00 an eine Double-Variable übergeben wird. Dort kommen dann ,291666666666667 an. Selbst im Windows-Taschenrechner kann man das nachvollziehen. Multipliziert man diesen Wert mit 24, erhält man saubere 7. Die Int-Funktion der wissenschaftlichen Darstellung stutzt das dann wieder auf 6 zurecht !?

Der berühmte kleine Unterschied liegt darin, ob eine Zahl wie 0,2916666666667 dadurch zustandekommt, daß der Anwender eine 11te '6' eingegeben hat und die Software das mit einer finalen '7' quittert, im Gegensatz zu einer Eingabe, bei der eine '7' als letzte Ziffer eingegeben wird. In beiden Fällen ergibt eine Multiplikation mit 24 eine scheinbar saubere 7. Die Int-Funktion trennt dann aber die Spreu vom Weizen.

Solange man mit dem nativen Zeitformat hh:nn klarkommt, klappt's auch mit den Stunden.

Und nun doch ein passables Workaround:

Function Get_SumDauer (dblDauer As Double) As String

    Dim Minuten As Long, Stunden As Long

    Minuten = dblDauer * 24 * 60
    Stunden = Int(Minuten / 60)

    Get_SumDauer = Format$(Stunden, "#,###") & ":" & Format$(dblDauer, "nn")

End Function

Das Aufmultiplizieren auf Minutenwerte mit Übergabe an eine LONG-Variable erzwingt eine Rundung der Double-Zeitwerte auf eine zumutbare Genauigkeit. Und wenn ich dann z.B. 420 Minuten durch 60 teile, kommt auch wirklich 7 raus. Davon ist dann auch der ganzzahlige Anteil 7. Selbstredend kommt diese Routine auch mit Werten > 23:59 klar.

Voila'


Werte   Quelle: dmt   Datum: 02.2005   nach oben

LOGISCHE VARIABLEN: WAHR / FALSCH, TRUE / FALSE

oder auch: Was lange währt, wird endlich falsch ...

Logische Variablen werden in Access über Integer verarbeitet (schnell isses ja).
Einen entsprechenden Datentyp (BOOLEAN) kennt Access2.0-Basic im Gegensatz zur eigenen Datenbankengine oder zu Open Access (jaja) nicht.

Interessant sind die Wahrheitszustände dieser Integer-Variablen:

Ist der Wert 0, so ist die Variable falsch.
Ist der Wert

<>
0, so ist die Variable NICHT falsch.

Ein allgemeines 'Wahr' ist also nicht unbedingt an den Basic-Wert -1 gebunden, sondern es reicht aus, daß der Variable überhaupt ein Wert

<>
0 zugewiesen wurde.

Der explizite Vergleich

x = True
gelingt in Basic jedoch nur, wenn x den Wert -1 besitzt.

Das macht dann auch treffsicher Ärger mit der SQL-Engine:

Ich wollte ein Integer-Feld benutzen um zwischen -1 und Platzierungs-Werten > 0 zu differenzieren.

Ein

SELECT *
FROM Tabelle
WHERE Wert = True;

findet auch Datensätze mit z.B. Wert=3.

Erst ein explizites "WHERE Wert = -1" machts das dann auch mit der True-Scheiße richtig und kann zwischen -1 und anderen Zahlen unterscheiden !


UNBEDINGT BEACHTEN:

If Wert then MsgBox "Ja !"           ' zeigt die msgbox an, wenn Wert 
<>
0 ist.

If Wert = true then MsgBox "Ja !" ' zeigt die msgbox nur dann, wenn Wert = -1 ist.

Also auf keinen Fall 'If' mit dem Wert 'true' verwechseln, am besten immer arschlochmäßig nach dem Motto 'if Variable = x then ... " programmieren; so kann wohl nicht viel schief gehen.

Aber das ist noch nicht alles zum Thema Umgang mit boolschen Variablen:

Wenn in einem Formular eine Optionshäkchen-Box (Optionsfeld) mit dem Wert 1 versehen wird und diese Einstellung per Code abgefragt wird, stehen wir vor dem selben Problem wie oben beschrieben. Das Optionsfeld ist immer angekreuzt, wenn sein Wert verschieden von 0 ist. Aber das kann leider nicht mit den Werten True oder False verglichen werden. Anstatt mit Zahlenwerten zu operieren, ist es besser, der Eigenschaft "Standardwert" ein TRUE oder FALSE zuzuweise. Innerhalb der Entwicklungsumgebung wird das dann zu einem "Wahr" oder "Falsch" umgemostet, aber das sollte nicht weiter belasten.

Ich erinnere mich da an eine Software, die solche Probleme nicht kannte, lang, lang ist's her ...


Werte, verlieren   Quelle: dmt   Datum: 03.2004   nach oben

VERLUST der VARIABLENWERTE / VARIABLENINHALTE:

kommt zuweilen immer wieder mal ab und zu vor; nicht nur beim Entwickeln sondern auch schon mal beim Anwender. Nicht Abhilfe, aber wenigstens doch Erhellung bringt folgende Routine, die von geeigneten Stellen aus aufgerufen (z.B. Form_Current in Adressen oder Auftrag mit z.B. formular-globalen Variablen als Parameter) den hoffentlich intakten Zustand der Basic-Umgebung prüfen.

Sub Check_Basic (v As Variant)

    If FlascheLeer(v) Then
       Beep
       MsgBox "In dieser Anwendung ist ein Fehler aufgetreten." & Chr$(13) & Chr$(10) & Chr$(13) & Chr$(10) & "Wenn diese Meldung häufiger erscheint, sollten Sie versuchen, die vorangegangenen Arbeitsabläufe zu beschreiben und den Programmierer dieser Anwendung zu benachrichtigen." & Chr$(13) & Chr$(10) & Chr$(13) & Chr$(10) & "Sie können mit dieser Anwendung nach erneutem Aufruf bedenkenlos weiterarbeiten.", 16, "Verlust der Code-Variablenwerte"
    End If

End Sub

In einem Fall konnte das Verlustiggehen der Code-Werte sogar regelmäßig wiederholt werden. s. UNTERFORMULARE lassen sich ebenfalls als UNTERBERICHTE einsetzen.

* * * *

In der VB6-Anwendung "PMFRes" der Fa. vector hat mich im Zusammenhang mit einem Dictionary-Objekt folgendes zu schierer Wut veranlaßt:

Eine aufgerufene Routine füllt die dictionary-Liste mit Daten, alles ist wunderbar, aber später sind die Daten dieses dictionary-Objektes nicht mehr abrufbar, obwohl die Eigenschaft "Count" immer noch die korrekte Anzahl der Listeneinträge liefert.

Es hat einen halben Tag gedauert, bis ich den Grund heraus fand:
Die Werte wurden an das dictionary-Objekt (das aus meiner Sicht einem "assoziativen Array" wie z.B. aus php bekannt, gleicht) in der Form rs!feld übergeben. Als die Recordset-Schleife abgearbeitet war, konnten die Werte des dictionary-Objektes nicht mehr angesprochen werden und die Entwicklungsumgebung beharrte darauf, den Fehler "Kein aktueller Datensatz" zu bringen. Erst, nachdem die Werte per rs!feld.Value zugewiesen wurden, hörte der Spuk auf.

Was ist (wahrscheinlich) passiert ?
Die direkte Zuweisung kam wahrscheinlich per (undokumentierter) Referenz-Zuweisung an. Innerhalb der Recordset-Schleife waren auch noch alle dictionary-Einträge abrufbar. Am Ende der Schleife trat rs.EOF ein, nach Verlassen der Routine war natürlich von dem unsichtbar verzahntem rs-Recordset keine Spur mehr. Die Zuweisung per .Value hat offensichtlich das bewirkt, das man bei Werte-Übergabe per Parameter als "als Wert übergeben" (ByVal) bezeichnet.

* * * *

Und gleich nochmal was zum VB6-dictionary-Objekt (ebenfalls pmfres):

Beim Debuggen der Werte eines dictionary-Objektes mußte ich feststellen, daß, wenn ich bei einer 6 Einträge enthaltenden dictionary-Liste den 7. Wert abfrage, ich nicht nur ein "" als Ergebnis erhalte, sondern daß das dictionary um genau diesen einen Eintrag erweitert wurde.

Das bedeutet, daß das Lesen von teilweise nicht vorhandenen Werten eine schreibende Manipulation der Daten bewirkt !

Ihr Microsoft-Idioten, seid Ihr eigentlich noch ganz dicht ?

Schaut Euch doch mal z.B. in PHP an, wie man sog. "assoziative Arrays" in Programmiersprachen implementiert, und das incl. geiler Methoden und dabei ohne das OOP-Fähnchen (Objekt-orientiertes Programmieren) so in den Wind zu hängen, wie Ihr es seit Jahren tut.

nach oben
zur Startseite dieses Webangebotes zur infobase-Hauptseite