Mehrere BIT-Datentypspalten in derselben Tabelle

7

Beachten Sie die folgenden Informationen, die von Microsoft SQL-Dokumentenseiten stammen:

Das SQL Server-Datenbankmodul optimiert die Speicherung von Bitspalten. Wenn eine Tabelle 8 oder weniger Bitspalten enthält, werden die Spalten als 1 Byte gespeichert. Wenn 9 bis 16-Bit-Spalten vorhanden sind, werden die Spalten als 2 Byte usw. gespeichert.

Siehe: https://docs.microsoft.com/en-us/sql/t-sql/data-types/bit-transact-sql?view=sql-server-2017

Wenn ich also eine neue Tabelle wie diese erstelle:

CREATE TABLE MyTable (
    Id int PRIMARY KEY,
    Value1 bit,
    Value2 bit,
    Value3 bit,
    Value4 bit,
    Value5 bit,
    Value6 bit,
    Value7 bit,
    Value8 bit)

... dann sollten alle Bitspalten (Wert1, Wert2, ... Wert8) in einer Zeile ein Byte einnehmen. Oder?

Bei näherer Betrachtung kann das Feld Wert1 einer Zeile die Werte 0, 1 oder NULL enthalten. Dies bedeutet, dass das Feld tatsächlich einen Wert mit drei Zuständen oder einen ternären Wert hat. 2 solche Werte können ein Wörterbuch mit 3 * 3 = 9 Werten darstellen. 8 solche Werte sind ein Wörterbuch mit 6561 Werten.

Wenn wir über den Speicherbedarf einer solchen Zeile nachdenken, sollten 8 Bits ein Byte einnehmen. Dies bedeutet, dass dieses eine Byte 6561 Werte anstelle von 256 Werten codiert. Das ist offensichtlich falsch.

Entweder sind MS SQL-Dokumente irreführend, oder wir sprechen hier von Quantencomputern. Natürlich ist es das erstere, aber was ich wirklich wissen möchte, ist die Antwort auf diese Frage: Wie werden nullbare Bits (und natürlich, dass SQL Server-Bits standardmäßig nullbar sind) wirklich in zugrunde liegenden SQL Server-Strukturen gespeichert?

OzrenTkalcecKrznaric
quelle
1
Stellen Sie sich NULL am besten nicht als Wert eines Attributs vor, sondern als Metadaten zum Status dieses Attributs.
Michael Green

Antworten:

13

Bei einem Feld mit fester Länge (z. B. einem Bitwert) nimmt das Feld immer den gleichen Speicherplatz ein - in Ihrem Fall ein Bit.

Die NULL-Bitmap befindet sich in der Datenzeile selbst - diese wird gespeichert, unabhängig davon, ob ein bestimmtes Feld als NULL betrachtet wird oder nicht. Als solches muss das Feld selbst nur zwei Werte speichern - 1 oder 0 - die NULL-Bitmap behandelt, ob das Feld tatsächlich auf NULL gesetzt ist oder nicht.

Die NULL-Bitmap enthält für jede Spalte in der Tabelle ein Bit, unabhängig davon, ob die Spalte nullwertfähig ist, und enthält den von Ihnen erwähnten "dritten Wert". Wiederum wird der Speicherplatz für die Null-Bitmap auf eine ganzzahlige Anzahl von Bytes aufgerundet.

Der folgende Code zeigt, wie SQL Server die NULL-Bitmap speichert. Sie müssen die Seitenzahlen ersetzen, wenn Sie diese selbst ausführen.

CREATE TABLE TestNullBitmap (
    TestText CHAR(10) NULL
    )
GO

INSERT INTO [dbo].[TestNullBitmap] ([TestText])
VALUES
('hello'),
('hello2'),
(NULL);

--Grab the page number
DBCC IND ([TestDb],TestNullBitmap,-1);
GO

--Use the page number from DBCC IND
DBCC PAGE ([TestDb],1,631032,3) WITH TABLERESULTS;
GO

Für mich erhalte ich folgende Ergebnisse für die drei Datenzeilen:

0000000000000000: 10000e00 68656c6c 6f202020 20200100 00 .... hallo ...

0000000000000000: 10000e00 68656c6c 6f322020 20200100 00 .... hello2 ...

0000000000000000: 10000e00 68656c6c 6f322020 20200100 01 .... hello2 ...

Die NULL-Bitmap ist die Änderung des letzten Bytewerts - in Little Endian wird die Binärdarstellung gelesen 0000 0001(IE Die erste Spalte ist NULL).

Interessanterweise können Sie auch sehen, dass das System den Wert aus dem vorherigen INSERT als die tatsächlichen Daten für das Feld mit fester Länge gespeichert hat - anstatt ihn auszulöschen.

George.Palacios
quelle
Ist es nicht verschwenderisch, ein Bit in der NULL-Bitmap für nicht nullfähige Spalten zu haben? Wird dies getan, um das Ändern dieses Attributs zu ermöglichen, ohne die Tabelle neu erstellen zu müssen?
Barmar
@barmar Ich würde eher zustimmen - das könnte seine eigene Frage wert sein.
George.Palacios