none
Bitweise Operationen in VBA

    Frage

  • Hallo alle miteinander,

    ich hatte angekündigt, beim nächsten (also diesem) Mal "ein, in akzeptablem Zeitumfang, lösbares Problem zu präsentieren". Ich hoffe, dass mir dies hiermit gelungen ist:

    Vorab, ich hasse alle Sprachen, die ein "BASIC" in ihrem Namen führen. Ich habe bloß leider keine Wahl und muss nehmen, was man mir vorgibt, jetzt also VBA in Access 2003 bzw. 2010.

    Ich führe in meinen Tabellen Bitmasken mit, in denen ich Bits einzeln setzen, rücksetzen und/oder auswerten will. Diese Bitmasken sind definiert als

    Feldname:                   BitMask

    Typ:                             Long Integer

    Format:                       nicht definiert

    Standardwert:            0

    Rest:                           vermutlich uninteressant

    Der Ärger fängt schon beim ersten Setzen eines Bits mit folgender, hier verkürzten und vereinfachten, SQL-Anweisung an:

    DoCmd.RunSQL "UPDATE Erfassungen SET Erfassungen.BitMask = (Erfassungen.BitMask OR 1);"

    Ihr könnt es Euch sicher denken: Das Ergebnis ist nicht etwa 1, sondern -1! ...weil es eben, als logischer Wert behandelt und eingetragen, TRUE ergibt. Und das ist vollkommen egal, ob ich mit 1 oder mit &H1 oderiere. Aber nur beim Eintrag in das Feld. Debugge ich das Programm und gebe mir das Ergebnis der Operation vor dem Eintrag in die Tabelle aus, dann ist es 1. In der Tabelle steht dann aber -1.

    Ich kann zwar OR und AND durch Addition und Subtraktion simulieren. Das will ich aber nicht, weil ich eben OR und AND meine und nicht + und -!

    Im Direktfenster funktioniert das alles noch normal: 17 AND 1 ergibt dort 1 und 17 OR 8 ergibt dort 25.

    Liegt es nun an meinen Tabellen? Woran dann aber? Oder an SQL? Wieso dann aber?

    Kann mir das jemand verständlich erklären???

    Einen trotzdem sonnigen Tag wünscht Euch


    Werner Muehlmann

    Mittwoch, 25. April 2012 06:03

Antworten

  • WerMily wrote:

    DoCmd.RunSQL "UPDATE Erfassungen SET Erfassungen.BitMask =
    (Erfassungen.BitMask OR 1);"

    Ja, dass Du eine Abneigung gegen VBA hast sieht man an diesem Statment gleich zweimal.
    1. Du verwendest den Command Interpreter, statt die üblichen VBA Befehle
    2. OR gibt es eben in JET SQL nicht, nur in VBA

    Die Lösung ist in VBA ziemlich einfach.

    Leg' Dir ein neues, ungebundenes Standard Modul an. Dort leg eine Funktion rein, die folgendermassen aussieht:

    Public Function blnOr(Par1 As long, Par2 As Long)
     blnOr = (Par1 Or Par2)
    End Function

    Und dann sieht obige Zeile so aus:

    Dim strSQL As String
    strSQL = "UPDATE Erfassungen SET BitMask = blnOr(BitMask, 1)"
    CurrentDB.Execute strSQL, dbFailOnError

    Schon fertig und das mit VBA

    Gruss
    Henry


    Donnerstag, 26. April 2012 04:19

Alle Antworten

  • Hallo!

    Grundproblem: Bitmasken widersprechen der Normalisierung. :)

    Aber nun zu deiner Problemstellung: Das "OR" in Jet-SQL (hier kommt zum Glück das Wort BASIC nicht vor ;)) ist kein Operator für bit-Masken.

    Der Grund für das "-1" ist, dass der logische Ausdruck "(Erfassungen.BitMask OR 1)" in Jet-SQL (das ist nicht VBA!) immer True ergibt, weil 1 immer zu True wird.

    Du könntest ADODB statt DAO verwenden, dann würde dir der Operator BOR zur Verfügung stehen.

    CurrentProject.Connection.Execute "UPDATE Erfassungen SET Erfassungen.BitMask = (Erfassungen.BitMask BOR 1)"
    mfg

    Josef


    Code-Bibliothek für Access-Entwickler
    AccUnit - Testen von Access-Anwendungen
    Virtueller Access-Stammtisch



    Mittwoch, 25. April 2012 07:17
  • Hallo Josef,

    aua, das mit der Normalisierung hat gesessen! Ich hatte aber 1979 mit Assembler angefangen. Krieg mal aus so einem die Bitvorstellungen wieder raus!!

    Aber im Ernst. Hatte nicht Goethe schon Faust sagen lassen:

    Ein großer Irrtum. Wer befehlen soll,
    Muss im Befehlen Seligkeit empfinden.
    Ihm ist die Brust von hohem Willen voll,
    Doch was er will, es darf’s kein Mensch ergründen.

    Und befehlen ist doch nichts anderes als programmieren, oder?

    Ich will also trotzdem, Normalisierung hin und her, und Seligkeit empfinden. Deshalb habe ich mich trotz aller Nachteile entschlossen AND und OR durch - und + zu ersetzen. Die Nachteile, keine direkte Zuordenbarkeit der Operationen und damit leidende Verständlichkeit des Programmes sowie, und das ist das Entscheidendste, die geringere Performance, habe ich in Funktionen verpackt, die mir für das Rücksetzen des Bits aus

    ehemals

    BitMask = (BitMask AND NOT SetBit)

    jetzt

    BitMask = IIF(BitMask AND SetBit, BitMask - SetBit, BitMask)

    und für das Setzen des Bits aus

    ehemals

    BitMask = (BitMask OR SetBit)

    jetzt

    BitMask = IIF(BitMask AND SetBit, BitMask, BitMask + SetBit)

    macht.

    Für das, was ich vorhabe, werde ich zähneknirschend mit den Nachteilen leben.

    Danke auch für Deinen Hinweis zu ADODB. Aber deswegen werde ich ADODB nicht extra einführen.

    Für bessere Vorschläge bin ich weiterhin zu haben.

    Mit besten Grüßen


    Werner Muehlmann


    Mittwoch, 25. April 2012 11:54
  • Hallo!

    Was meinst du mit "ADODB einführen"? Das ist doch schon längst enthalten.
    Hast du dir den Code von mir angesehen? (Der läuft auch ohne Verweis auf ADODB.)

    Ansonsten:
    In VBA kannst du OR und AND bzw. XOR schon als Bit-Operatoren verwenden. Nur In Jet-SQL ist das nicht möglich.

    mfg
    Josef


    Code-Bibliothek für Access-Entwickler
    AccUnit - Testen von Access-Anwendungen
    Virtueller Access-Stammtisch


    Mittwoch, 25. April 2012 13:43
  • Danke nochmals, Josef.

    Mit "einführen" meinte ich "verwenden". Ich habe mich bisher strikt an DAO gehalten, etwa nach dem Motto "Wenn schon VBA, dann wenigstens sauber". Mit "sauber" meinte ich hier, "so übersichtlich, wie nur möglich" oder "so wenig Sprachbestandteile, wie nur möglich". Ich hoffe, Du kannst mich verstehen. Aber da ich keine Frau bin (  ;-))  ), halte ich mich für lernfähig. Falls mich also tatsächlich jemand von ADO überzeugen werden sollte, würde ich mich nicht mit Händen und Füßen wehren...

    Beste Grüße


    Werner Muehlmann

    Mittwoch, 25. April 2012 14:21
  • Hallo Josef,

    ich würde mir trotzdem einmal den Code von Dir ansehen wollen (wegen der Lernfähigkeit...). Allerdings finde ich nicht den schnellen Einstieg. Vielleicht kannst Du mir noch einen Wegweiser hinterher schieben, der es mir gestattet, schnell zu dem Punkt zu gelangen, den Du oben gemeint hast?

    Werner


    Werner Muehlmann

    Mittwoch, 25. April 2012 14:27
  • Hallo!

    DAO ist für mich:

    DoCmd.RunSQL "UPDATE Erfassungen SET Erfassungen.BitMask = (Erfassungen.BitMask BOR 1)"

    ADODB/OLEDB wird hier verwendet:

    CurrentProject.Connection.Execute "UPDATE Erfassungen SET Erfassungen.BitMask = (Erfassungen.BitMask BOR 1)"

    .. beides sind über die in Access eingebauten Elemente erreichbar.

    Vorteil von ADODB im Vergleich zu DAO: es unterstützt etwas mehr SQL-Befehle, die Jet versteht.
    Anm.: vor allem bei DDL-Anweisungen kann man mit ADODB mehr machen.

    mfg
    Josef


    Code-Bibliothek für Access-Entwickler
    AccUnit - Testen von Access-Anwendungen
    Virtueller Access-Stammtisch


    Mittwoch, 25. April 2012 14:54
  • WerMily wrote:

    DoCmd.RunSQL "UPDATE Erfassungen SET Erfassungen.BitMask =
    (Erfassungen.BitMask OR 1);"

    Ja, dass Du eine Abneigung gegen VBA hast sieht man an diesem Statment gleich zweimal.
    1. Du verwendest den Command Interpreter, statt die üblichen VBA Befehle
    2. OR gibt es eben in JET SQL nicht, nur in VBA

    Die Lösung ist in VBA ziemlich einfach.

    Leg' Dir ein neues, ungebundenes Standard Modul an. Dort leg eine Funktion rein, die folgendermassen aussieht:

    Public Function blnOr(Par1 As long, Par2 As Long)
     blnOr = (Par1 Or Par2)
    End Function

    Und dann sieht obige Zeile so aus:

    Dim strSQL As String
    strSQL = "UPDATE Erfassungen SET BitMask = blnOr(BitMask, 1)"
    CurrentDB.Execute strSQL, dbFailOnError

    Schon fertig und das mit VBA

    Gruss
    Henry


    Donnerstag, 26. April 2012 04:19
  • Mensch, Henry,

    warum bin ich da nicht selber drauf gekommen????

    Danke! Das überzeugt mich ungeprüft. Nach meinem Urlaub werde ich das sofort übernehmen.

    Nochmals danke!


    Werner Muehlmann

    Donnerstag, 26. April 2012 04:37
  • Ein Psychologe würde sagen, dass das eine mentale Blockade war, die aufgrund Deiner VBA-Abneigung, welcher Du hier Kund getan hast, entstanden ist.

    Also "Basic" Abneigung abstreifen und dessen Einfachheit geniessen.

    Noch einen schönen Urlaub und Grüsse aus Phuket

    Henry

    Donnerstag, 26. April 2012 04:48
  • Also darauf muss ich doch noch einmal reagieren.

    1. Wie markiere ich denn die Angelegenheit als erledigt?

    2. und damit das Eigentliche: Um Freud eine Freude zu tun, gebe ich gern meine inneren Beweggründe preis: Mit der mentalen Blockade lagst Du gar nicht so daneben. Noch treffender aber ist das: Ich wollte einfach nicht zulassen, dass es in einer weit verbreiteten Programmiersprache, und unter Access sehe ich in diesem Zusammenhang auch die zunächst naheliegendste Datenbankgrundlage, also JET, inbegriffen, eine der einfachsten und urältesten Operationen nicht geben soll, nämlich OR (oder auch AND). Ich habe einfach an mir selbst gezweifelt. Ich meinte, es müsse das Sprachkonstrukt irgendwie geben und ich würde es nur nicht kennen. Die nun bittere Erkenntnis bringt mir VBA nicht gerade näher, auch wenn es nun eine Lösung gibt ... die übrigens wesentlich besser als meine oben angeführte ist!

    Werner


    Werner Muehlmann

    Donnerstag, 26. April 2012 05:22
  • Hallo!

    Zur Verteidigung von Jet: Jet kenn BOR (Bitwise OR), nur DAO versteht das nicht. ;-)
    Warum DAO bezüglich SQL-Syntax "doofer" als ADODB ist, ist mir auch unklar - besonders unter dem Gesichtspunkt, dass seit Access 2007 DAO wieder als Standard für Datenzugriffe empfohlen wird (ACEDAO als neue Version).

    Bezüglich "Wie markiere ich denn die Angelegenheit als erledigt?":
    Du kannst bei einem Beitrag (in der Fußzeile jedes Beitrags) auf "Als Antwort markieren" klicken.

    mfg
    Josef


    Code-Bibliothek für Access-Entwickler
    AccUnit - Testen von Access-Anwendungen
    Virtueller Access-Stammtisch

    Donnerstag, 26. April 2012 05:30
  • Hallo Werner

    Zu 1

    Das geht nur im Forum selber. Wenn Du einen Newsreader verwendest, dann geht das nicht.

    So geht's im Form: Du markierst einfach mein Posting als Antwort (das von Josef wäre ja auch eine, Du kannst also mehrere als Antwork markieren oder vorschlagen). Und wenn die Antwort noch hilfreich war, kannst Du dem auch noch kundtun, indem Du unter meinem oder Josephs Bildchen noch auf das grüne Preilchen klickst (als Hilfreich bewerten).

    Zu 2

    Jet wird zwar mit Access ausgeliefert und Access basiert auf Jet. Aber Jet ist nicht von den Access Leuten, sondern von der SQL Server Gruppe. Erst die ACE (Access Data Engine) ist in der Verantwortung der Access Programm Gruppe. Das erklärt da einige Unterschiede. Jet wird übrigens bereits bei der Installation des Betriebssystems installiert und kommt nicht erst mit Access auf den Rechner drauf. Jet wird von anderen Komponenten ebenfalls verwendet.

    Nun zu den binären Operatoren in SQL: Wenn der Bedarf and binären Operatoren (AND, OR, NAND, XOR, etc) besteht, dann ist der Verdacht begründet, dass es sich hier um eine Verletzung der Normalformen handelt und mehrere Dateneinheiten in ein einzelnes Datenfeld gezwängt wurden, welche eigentlich in mehrere Datenfelder als Bit Datentyp abgelegt werden sollten, welche dann True oder False (oder NULL im Fall von Jet) sein können. Darum ist es erklärlich, dass Jet über DAO keine Binären Operatoren zur Verfügung stellt. Können tut es es schon, aber eben, es stellt diese nicht zur Verfügung. Über ADODB/OLEDB (neuer) werden diese Operationen - wie Josef geschrieben hat - zur Verfügung gestellt. Dafür auf ADODB/OLEDB zu wechseln halte ich allerdings nicht unbedingt für sinnvoll, weil der Code damit nicht übersichtlicher wird.

    Langer Rede kurzer Sinn: Wenn Du Bedarf an binären Operatoren hast, dann solltest Du nicht diese suchen, sondern das Datenmodell genauer unter die Lupe nehmen, was von Josef ebenfalls schon angetönt wurde.

    Gruss

    Henry

    Donnerstag, 26. April 2012 05:42
  • Hallo Henry!

    [OT]

    zu Übersichtlichkeit im Code:

    CurrentProject.Connection.Execute "UPDATE Tabell SET XYZ = 123"

    finde ich nicht weniger übersichtlich als

    CurrentDb.Execute "UPDATE Tabell SET XYZ = 123", dbfailonerror

    Ich selbst verwende DAO und ADODB gemischt in meinen Anwendungen, da ich auf den MSSQL-Server lieber per OLEDB zugreife. Allerdings nutze ich Wrapper-Klassen, um eine einheitlich Schnittstelle zu erhalten.

    Obiger Code würde bei mir z. B. so aussehen:

    Data.ADODB.Execute "UPDATE Tabelle SET XYZ = 123"

    bzw.

    Data.DAO.Execute "UPDATE Tabelle SET XYZ = 123"

    mfg
    Josef


    Code-Bibliothek für Access-Entwickler
    AccUnit - Testen von Access-Anwendungen
    Virtueller Access-Stammtisch

    Donnerstag, 26. April 2012 05:56
  • Hallo Josef

    Josef Pötzl [MVP] wrote:

    CurrentProject.Connection.Execute "UPDATE Tabell SET XYZ = 123"

    finde ich nicht weniger übersichtlich als

    CurrentDb.Execute "UPDATE Tabell SET XYZ = 123", dbfailonerror

    Ich meine nicht, dass das Statement unübersichtlicher aussieht, sondern dass die Ganze Anwendung unübersichtlicher wird, weil man dann immer wieder überlegen muss: Bin ich jetzt in DAO oder ADODB? Entweder oder, aber ich würde nicht mischen. Und wie Du bereits angetönt hast, ist eben DAO nun gemäss den Fahnen im Wind von Microsoft wieder das künftig alleinig glücklich machende. Also lasse ich es bei mir dabei.

    PS: Wrapper finde ich übrigens auch nicht immer übersichtlichkeitsfördernd, aber das ist wie DAO oder ADO eben oft auch Geschmacksache ;-)

    Gruss
    Henry

    Donnerstag, 26. April 2012 06:00