Microsoft SQLのドキュメントページからの次の情報を検討してください。
SQL Serverデータベースエンジンは、ビット列のストレージを最適化します。テーブルに8ビット以下の列がある場合、列は1バイトとして格納されます。 9ビットから16ビットまでの列がある場合、列は2バイトとして格納されます。
参照: https://docs.Microsoft.com/en-us/sql/t-sql/data-types/bit-transact-sql?view=sql-server-2017
したがって、次のような新しいテーブルを作成すると、
CREATE TABLE MyTable (
Id int PRIMARY KEY,
Value1 bit,
Value2 bit,
Value3 bit,
Value4 bit,
Value5 bit,
Value6 bit,
Value7 bit,
Value8 bit)
...次に、1つの行のすべてのビット列(Value1、Value2、... Value8)は1バイトを占める必要があります。それとも?
よく見てみると、行のValue1フィールドは、内部に値0、1、またはNULLを持つことができます。つまり、フィールドは実際にはトライステート値または3値です。そのような2つの値は、3 * 3 = 9の値の辞書を表すことができます。 8個のそのような値は6561個の値の辞書です。
このような行のメモリフットプリントを考えている場合、8ビットは1バイトを占めるはずです。つまり、この1バイトは256値ではなく6561値をエンコードします。これは明らかに間違っています。
したがって、MS SQLドキュメントは誤解を招くものであるか、ここでは量子コンピューティングについて話しています。もちろんそれは前者ですが、私が本当に知りたいのは、この質問に対する答えです:null可能ビットはどのように(そしてもちろん、SQL Serverビットはデフォルトでnull可能です)reallySQL Serverの基本構造に格納されていますか?
固定長フィールド(ビット値など)の場合、フィールドは常に同じ量のスペースを使用します。この場合、1ビットです。
NULLビットマップ はデータ行自体にあります。これは、特定のフィールドがNULLと見なされるかどうかに関係なく格納されます。そのため、フィールド自体は2つの値(1または0)を格納するだけでよく、NULLビットマップは、フィールドが実際にNULLに設定されているかどうかに関係なく処理します。
NULLビットマップは、列がNULL可能かどうかに関係なく、テーブルの各列にビットを持ち、これには、あなたが言及する「3番目の値」が含まれます。ここでも、nullビットマップのスペースは、バイト数の整数に切り上げられます。
次のコードは、SQL ServerがNULLビットマップを格納する方法を示しています。これを自分で実行する場合は、ページ番号を置き換える必要があります。
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
私にとっては、3つのデータ行について次の結果が得られます。
0000000000000000:10000e00 68656c6c 6f202020 20200100 00 .... hello ...
0000000000000000:10000e00 68656c6c 6f322020 20200100 00 .... hello2 ...
0000000000000000:10000e00 68656c6c 6f322020 20200100 01 .... hello2 ...
NULLビットマップは、その最後のバイト値への変更です。リトルエンディアンで読み取ると、バイナリ表現は0000 0001
(IE最初の列はNULLです)
興味深いことに、システムが前のINSERTの値を、固定長フィールドの実際のデータとして、0にするのではなく保存したこともわかります。