Benutzer mit den meisten Antworten
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
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 VBADie 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- Bearbeitet Henry Habermacher Donnerstag, 26. April 2012 04:22
- Als Antwort markiert Werner Mühlmann Donnerstag, 26. April 2012 06:17
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)"
mfgJosef
Code-Bibliothek für Access-Entwickler
AccUnit - Testen von Access-Anwendungen
Virtueller Access-Stammtisch
- Bearbeitet Josef Pötzl Mittwoch, 25. April 2012 07:18
-
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
- Bearbeitet Werner Mühlmann Mittwoch, 25. April 2012 11:57 Grammatikfehler
-
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- Bearbeitet Josef Pötzl Mittwoch, 25. April 2012 13:45
-
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
-
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
-
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
- Bearbeitet Josef Pötzl 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 VBADie 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- Bearbeitet Henry Habermacher Donnerstag, 26. April 2012 04:22
- Als Antwort markiert Werner Mühlmann Donnerstag, 26. April 2012 06:17
-
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
-
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
-
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
JosefCode-Bibliothek für Access-Entwickler
AccUnit - Testen von Access-Anwendungen
Virtueller Access-Stammtisch -
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
-
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- Bearbeitet Josef Pötzl 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