Considere as seguintes informações provenientes das páginas de documentos do Microsoft SQL:
O SQL Server Database Engine otimiza o armazenamento de colunas de bits. Se houver 8 ou menos colunas de bits em uma tabela, as colunas serão armazenadas como 1 byte. Se houver colunas de 9 a 16 bits, as colunas serão armazenadas como 2 bytes e assim por diante.
Consulte: https://learn.microsoft.com/en-us/sql/t-sql/data-types/bit-transact-sql?view=sql-server-2017
Então, se eu criar uma nova tabela assim:
CREATE TABLE MyTable (
Id int PRIMARY KEY,
Value1 bit,
Value2 bit,
Value3 bit,
Value4 bit,
Value5 bit,
Value6 bit,
Value7 bit,
Value8 bit)
...então todas as colunas de bits (Value1, Value2, ... Value8) em uma linha devem ocupar um byte. Ou?
Quando olhamos mais de perto, o campo Valor1 de uma linha pode ter valores 0, 1 ou NULL dentro. Isso significa que o campo é, na verdade, de um valor de 3 estados ou um valor ternário. 2 desses valores podem representar um dicionário de 3*3=9 valores. 8 desses valores são um dicionário de 6561 valores.
Se estivermos pensando no consumo de memória de tal linha, 8 bits devem ocupar um byte. Isso significa que esse byte codifica 6561 valores em vez de 256 valores. Isso está obviamente errado.
Então, ou os documentos do MS SQL são enganosos ou estamos falando de alguma computação quântica aqui. Claro que é o primeiro, mas o que eu realmente gostaria de saber é a resposta para esta pergunta: como os bits anuláveis (e, claro, os bits do SQL Server são anuláveis por padrão) realmente armazenados nas estruturas subjacentes do SQL Server?
Para um campo de comprimento fixo (como um valor de bit), o campo sempre ocupará a mesma quantidade de espaço - no seu caso, um bit.
O bitmap NULL está na própria linha de dados - isso é armazenado independentemente de um campo específico ser considerado NULL ou não. Como tal, o próprio campo só precisa armazenar dois valores - 1 ou 0 - o bitmap NULL trata se o campo está realmente definido como NULL ou não.
O bitmap NULL tem um bit para cada coluna na tabela, independentemente de a coluna ser anulável, e contém o "terceiro valor" que você menciona. Novamente, o espaço para o bitmap nulo é arredondado para um número inteiro de bytes.
O código a seguir demonstra como o SQL Server armazena o bitmap NULL - você precisará substituir os números de página se executar isso sozinho.
Para mim, recebo os seguintes resultados para as três linhas de dados:
O bitmap NULL é a mudança para esse último valor de byte - lido em Little Endian a representação binária é
0000 0001
(IE A primeira coluna é NULL)Curiosamente, você também pode ver que o sistema armazenou o valor do INSERT anterior como os dados reais para o campo de comprimento fixo - em vez de zerá-lo.