考虑来自 Microsoft SQL 文档页面的以下信息:
SQL Server 数据库引擎优化了位列的存储。如果表中有 8 个或更少的位列,则这些列存储为 1 个字节。如果有 9 到 16 位列,则这些列存储为 2 个字节,依此类推。
请参阅:https ://learn.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)
...那么一行中的所有位列(Value1,Value2,...Value8)应该占用一个字节。或者?
当我们仔细观察它时,一行的 Value1 字段内部可以有值 0、1 或 NULL。这意味着该字段实际上是三态值或三元值。2 个这样的值可以表示 3*3=9 个值的字典。8 个这样的值是 6561 个值的字典。
如果我们考虑这样一行的内存占用,8 位应该占用一个字节。这意味着这一字节编码了 6561 个值而不是 256 个值。这显然是错误的。
因此,要么 MS SQL 文档具有误导性,要么我们在这里谈论一些量子计算。当然是前者,但我真正想知道的是这个问题的答案:可空位(当然 SQL Server 位在默认情况下可以为空)如何真正存储在 SQL Server 底层结构中?
对于固定长度的字段(例如位值),该字段将始终占用相同数量的空间 - 在您的情况下为一位。
NULL 位图位于数据行本身中 - 它存储了特定字段是否被视为 NULL。因此,字段本身只需存储两个值 - 1 或 0 - NULL 位图处理字段是否实际设置为 NULL。
NULL 位图对于表中的每一列都有一个位,无论该列是否可以为空,这包含您提到的“第三个值”。空位图的空间再次四舍五入为整数字节。
以下代码演示了 SQL Server 如何存储 NULL 位图 - 如果您自己运行,则需要替换页码。
对我来说,我得到三个数据行的以下结果:
NULL 位图是对最后一个字节值的更改 - 在 Little Endian 中读取二进制表示是
0000 0001
(IE 第一列是 NULL)相当有趣的是,您还可以看到系统已将前一个 INSERT 中的值存储为固定长度字段的实际数据 - 而不是将其置为 0。