Christoph Muthmann
Architektur und Administration
Architektur und Administration
Wenn man die binären Zustände vieler Felder ablegen möchte, oder entsprechende Felder einer Fremdsoftware auswerten möchte, bietet sich die Bitmaskierung an.
...
SQL Server bietet auch die Möglichkeit einzelne Felder vom Datentyp Bit zu deklarieren und verwaltet diese Felder platzsparend im Hintergrund für uns, aber hier erhält jedes Feld einen eigenen Namen, was nicht immer für die Programmierung von Vorteil ist. In diesem Artikel geht es also um die Verwaltung von Bits, wie sie z. B. in einem INTEGER-Wert repräsentiert werden.
Zahlen werden in der EDV im Dualsystem als Folge von Bits definiert. Hier findet man noch einmal die Hintergründe dazu!
Für die Darstellung der binären Zustände (z. B. aktiv/nicht aktiv) in Bezug auf die 7 Wochentage werden also 7 Bit benötigt. Jedes einzelne Bit steht für einen Wochentag, wobei das am weitesten rechts stehende Bit hier als Bit 1 bezeichnet wird.
Die Bitfolge 100 0101 entspricht also der Aktivierung der Bits für Tag 1, Tag 3 und Tag 7 der Woche. Die Umrechnung ins Dezimalsystem ergibt den Wert:
1 + 4 + 64 = 69
der uns aber in Bezug auf die codierten Zustände nicht weiterbringt.
Hier bieten sich verschiedene Wege an:
Beide Varianten habe ihre Vor- und Nachteile, wie die folgenden Beispiele zeigen.
Die erste Variante bedient sich der booleschen Algebra und verwendet den Ansatz, dass der Test, ob ein Bit gesetzt ist über eine UND-Verknüpfung eines Wertes mit dem entsprechenden Bit erfolgen kann. Im Dezimalsystem geschrieben lautet der Test:
69 AND 4
im Dualsystem sieht es dann so aus:
100 0101
AND 000 0100
------------
100CASE WHEN s.DaysOfWeek & 1 <> 0 THEN 'Sunday, ' ELSE '' END +
CASE WHEN s.DaysOfWeek & 2 <> 0 THEN 'Monday, ' ELSE '' END +
CASE WHEN s.DaysOfWeek & 4 <> 0 THEN 'Tuesday, ' ELSE '' END +
CASE WHEN s.DaysOfWeek & 8 <> 0 THEN 'Wednesday, ' ELSE '' END +
CASE WHEN s.DaysOfWeek & 16 <> 0 THEN 'Thursday, ' ELSE '' END +
CASE WHEN s.DaysOfWeek & 32 <> 0 THEN 'Friday, ' ELSE '' END +
CASE WHEN s.DaysOfWeek & 64 <> 0 THEN 'Saturday, ' ELSE '' END AS Days_of_Week_Desc
Das Ergebnis dieser Abfrage wäre dann bei dem Wert 69 für s.DaysOfWeek die Zeichenfolge:
Sunday, Tuesday, Saturday,
Beginnend von der höchsten zu erwartenden Potenz aus, prüfen wir, ob der Wert größer ist als der Vergleichswert. Ist dies der Fall, dann war also das Bit an der Position gesetzt, welches der nächsthöheren Potenz entspricht. Das gefundene Bit wird vom Wert abgezogen und der Vergleich geht weiter, bis wir bei der Potenz 0 ankommen.
Hiermit zerlegen wir den Wert 100 0101 in die Zahlen 2^6 + 2^2 + 2^0. Die Bitpositionen entsprechen der Potenz + 1 also die Werte 7, 3, 1.
Damit das Ergebnis schöner aussieht, reihen wir die gefundenen Bitpositionen vor das bisherige Ergebnis, so dass am Ende folgender Wert ausgegeben wird: 1, 3, 7.
Eine Umsetzung in sprechende Namen wie im ersten Beispiel ist hier erst mal nicht vorgesehen.
Falls wir größere Bereiche abbilden wollen, wie z. B. die Tage eines Monats 1..31, so wäre es durchaus wünschenswert, diese als Reihe dargestellt zu bekommen, falls diese Werte lückenlos besetzt sind. Die Darstellung als:
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
ist wenig übersichtlich, insbesondere wenn einzelne Werte fehlen sollten, sehr fehleranfällig. Wünschenswert ist hier die Darstellung:
1-31
Daher habe ich hier die folgende Vorgehensweise gewählt:
Ich habe diesem Artikel zwei Code-Beispiele angefügt, die für die beiden iterativen Varianten jeweils eine Funktion anlegen. Es empfiehlt sich eine Datenbank für administrative Tools und Skripte anzulegen, die unabhängig von irgendwelchen Anwendungen existieren kann.
Der dritte Code-Teil zeigt die Anwendung der Bit-Maskierung mit AND.
Was man dann mit diesen Funktionen z. B. bei der Auswertung der Reporting-Services erreichen kann, werde ich in einem meiner nächsten blog-Einträge beschreiben.
| Print article | This entry was posted by Christoph Muthmann on 16.04.12 at 12:21:00 . Follow any responses to this post through RSS 2.0. |
04.05.12 @ 14:31:44
Clever.
Eine Warnung vermisse ich aber leider und ergänze sie deshalb.
So verlockend das nämlich auf den ersten Blick erscheinen mag, handelt es sich doch letztlich um eine Verletzung der ersten Normalform, die ja besagt, dass jedes Attribut atomar sein soll.
In der Regel ist es keine gute Idee, diese Richtlinie zu verletzen.
Wenn man so etwas macht, dann ist die Verwendung von Funktionen zur Zerlegung der Information in die atomaren Bestandteile ein natürlicher Ansatz, der ebenfalls nicht ganz frei von Problemen ist. Bevor man sich versieht, stehen diese Funktionsaufrufe dann in Prädikaten. Der Optimierer hat dann keinerlei Möglichkeit, eine vernünftige Kardinalitätschätzung durchzuführen und der Ausführungsplan ist in der Regel suboptimal. Eine Index auf so einer Bit-codierten Spalte ist ebenfalls wenig sinnvoll.
Die Verwendung von benutzerdefinierten Funktionen in der SELECT-Liste ist ein weiterer Performance-Killer.
Also bitte mit Vorsicht verwenden.
04.05.12 @ 14:40:34
Vielen Dank für den Hinweis. Ich war auf umgekehrtem Wege zu dem Thema gekommen, da ich die Abonnements vom SSRS auswerten wollte.
Es gibt immer wieder Hersteller, die auf solchem Wege ihre Informationen codieren, bzw. verschleiern. Microsoft gehört bei den Reporting Services leider auch in diese Kategorie.