В этой статье я хочу развенчать распространённый миф о том, что null bitmap содержит биты только для допускающих NULL столбцов. Это не так — там по одному биту на каждый столбец в определении таблицы, при условии, что в таблице есть хотя бы один допускающий NULL столбец. «Неиспользуемые» биты всегда равны 1, то есть означают «NULL».
А теперь — доказательство. Я создам две таблицы по 10 столбцов в каждой (это означает, что для null bitmap потребуется два байта под все биты плюс два байта под счётчик столбцов в записи). Первая таблица будет с допускающими NULL столбцами. Во второй первые 9 столбцов будут NOT NULL, а десятый — допускающим NULL.
CREATE TABLE t1 (
c1 INT, c2 INT, c3 INT, c4 INT, c5 INT,
c6 INT, c7 INT, c8 INT, c9 INT, c10 INT);
GO
CREATE TABLE t2 (
c1 INT NOT NULL, c2 INT NOT NULL, c3 INT NOT NULL,
c4 INT NOT NULL, c5 INT NOT NULL, c6 INT NOT NULL,
c7 INT NOT NULL, c8 INT NOT NULL, c9 INT NOT NULL,
c10 INT);
GO
INSERT INTO t1 VALUES (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
GO
INSERT INTO t2 VALUES ( 1, 2, 3, 4, 5, 6, 7, 8, 9, NULL);
GO
Теперь посмотрим сами страницы. Я получу задействованные страницы с помощью моего скрипта sp_AllocationMetadata:
EXEC sp_AllocationMetadata 't1';
EXEC sp_allocationMetadata 't2';
GO
Object Name Index ID Alloc Unit ID Alloc Unit Type First Page Root Page First IAM Page
----------- -------- ------------------ ---------------- ----------- ---------- ---------------
t1 0 72057594042318848 IN_ROW_DATA (1:152) (0:0) (1:153)
Object Name Index ID Alloc Unit ID Alloc Unit Type First Page Root Page First IAM Page
----------- -------- ------------------ ---------------- ----------- ---------- ---------------
t2 0 2057594042384384 IN_ROW_DATA (1:154) (0:0) (1:155)
А теперь сделаем дамп страниц с помощью DBCC PAGE, чтобы посмотреть структуру строк:
DBCC TRACEON (3604);
GO
DBCC PAGE ('NullTest', 1, 152, 3);
DBCC PAGE ('NullTest', 1, 154, 3);
GO
-- Дамп записи из t1: все десять столбцов допускают NULL и не равны NULL
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x684EC060
00000000: 10002c00 01000000 02000000 03000000 †..,………….
00000010: 04000000 05000000 06000000 07000000 †…………….
00000020: 08000000 09000000 0a000000 0a0000fc †…………….
-- Дамп записи из t2: первые девять столбцов не допускают NULL, -- десятый столбец допускает NULL и равен NULL
Record Type = PRIMARY_RECORD Record Attributes = NULL_BITMAP
Memory Dump @0x684EC060
00000000: 10002c00 01000000 02000000 03000000 †..,………….
00000010: 04000000 05000000 06000000 07000000 †…………….
00000020: 08000000 09000000 21212121 0a0000fe †……..!!!!….
Если вы попробуете сами, вы можете увидеть иной шаблон бит для NULL в c10 (у меня это было 0x21212121). Тут всё зависит от того, какую память повторно использует SQL Server, создавая запись в памяти перед записью на страницу, — 4 байта NULL‑столбца не переписываются по сравнению с предыдущим использованием памяти (и в этом нет необходимости).
В первой записи видно, что null bitmap (выделенный полужирным) говорит о 10 столбцах (0x0a00 становится 0x000a при реверсе байтов), а сам null bitmap равен 0xfc00, то есть 1111110000000000 — 6 неиспользуемых бит, все равны 1, и 10 значений столбцов «не NULL».
Во второй записи видно, что null bitmap (выделенный полужирным) снова говорит о 10 столбцах, а сам null bitmap на этот раз равен 0xfe00, то есть 1111111000000000 — 6 неиспользуемых бит, все равны 1, 1 столбец равен NULL и 9 столбцов «не NULL». Столбец 1 — это крайний правый бит, значит столбец 10 — десятый справа (первая «1»).
Это наглядно показывает, что все столбцы представлены в null bitmap, даже если только один столбец в таблице допускает NULL. Ситуация будет такой же, если только первый столбец в таблице допускает NULL — оставляю это вам для самостоятельной проверки.
В SQL Server 2008, если все столбцы определены как SPARSE, null bitmap не будет вовсе — очень частный случай!

Комментариев нет:
Отправить комментарий