Зміна стовпця з NOT NULL на NULL - Що відбувається під кришкою?


25

У нас є таблиця з рядками 2.3B. Ми хотіли б змінити стовпець з NOT NULL на NULL. Стовпчик міститься в одному індексі (не кластеризованому або індекс PK). Тип даних не змінюється (це INT). Просто нульовість. Заява така:

Alter Table dbo.Workflow Alter Column LineId Int NULL

Операція займає понад 10, перш ніж ми її зупинимо (ми навіть не дозволили її виконати до завершення, оскільки це блокує операцію і зайняло занадто довго). Ми, ймовірно, скопіюємо таблицю на сервер розроблених тестів, скільки часу насправді потрібно. Але мені цікаво, якщо хтось знає, що робить SQL Server під кришкою при перетворенні з NOT NULL на NULL? Крім того, чи потребують відновлення індекси, які потребують відновлення? Створений план запитів не вказує, що відбувається.

Розглянута таблиця кластеризована (не купа).


2
Я думаю, що доведеться оновити нульову растрову карту на всіх сторінках даних на рівні листків. І, маючи 2.3B рядки, я думаю, що у нього буде багато сторінок для роботи. Я не надто впевнений у цьому.
souplex

3
Може бути також зайнято розміщенням нульового растрового зображення в індексі. Растровий файл NULL НЕ буде присутній у НЕКЛАСТИРОВАНОМ ІНДЕКСІ, якщо всі стовпці частини визначення індексу визначені як НЕ NULL.
суплекс

Відповіді:


27

Як згадує @Souplex в коментарях, одне можливе пояснення може бути, якщо цей стовпець є першим NULLстовпчиком стовпця в некластеризованому індексі, в якому він бере участь.

Для наступних налаштувань

CREATE TABLE Foo
  (
     A UNIQUEIDENTIFIER NOT NULL DEFAULT NEWSEQUENTIALID() PRIMARY KEY,
     B CHAR(1) NOT NULL DEFAULT 'B'
  )

CREATE NONCLUSTERED INDEX ix
  ON Foo(B);

INSERT INTO Foo
            (B)
SELECT TOP 100000 'B'
FROM   master..spt_values v1,
       master..spt_values v2 

sys.dm_db_index_physical_stats показує, що не кластеризований індекс ixмає 248 сторінок аркуша та одну кореневу сторінку.

Типовий рядок на сторінці вказівного аркуша виглядає так

введіть тут опис зображення

І в кореневій сторінці

введіть тут опис зображення

Потім працює ...

CHECKPOINT;

GO

ALTER TABLE Foo ALTER COLUMN B  CHAR(1) NULL;


SELECT Operation, 
       Context,
       ROUND(SUM([Log Record Length]) / 1024.0,1) AS [Log KB],
       COUNT(*) as [OperationCount]
FROM sys.fn_dblog(NULL,NULL)
WHERE AllocUnitName = 'dbo.Foo.ix'
GROUP BY Operation, Context

Повернувся

+-----------------+--------------------+-------------+----------------+
|    Operation    |      Context       |   Log KB    | OperationCount |
+-----------------+--------------------+-------------+----------------+
| LOP_SET_BITS    | LCX_GAM            | 4.200000    |             69 |
| LOP_FORMAT_PAGE | LCX_IAM            | 0.100000    |              1 |
| LOP_SET_BITS    | LCX_IAM            | 4.200000    |             69 |
| LOP_FORMAT_PAGE | LCX_INDEX_INTERIOR | 8.700000    |              3 |
| LOP_FORMAT_PAGE | LCX_INDEX_LEAF     | 2296.200000 |            285 |
| LOP_MODIFY_ROW  | LCX_PFS            | 16.300000   |            189 |
+-----------------+--------------------+-------------+----------------+

Перевірка вказівного листа знову виглядає як рядки

введіть тут опис зображення

і рядки на сторінках верхнього рівня, як показано нижче.

введіть тут опис зображення

Кожен рядок був оновлений і тепер містить два байти для підрахунку стовпців разом з іншим байтом для NULL_BITMAP.

Завдяки додатковій ширині рядків некластеризований індекс тепер має 285 сторінок аркуша, а тепер дві проміжні сторінки разом із кореневою сторінкою.

План виконання для

 ALTER TABLE Foo ALTER COLUMN B  CHAR(1) NULL;

виглядає так

введіть тут опис зображення

Це створює абсолютно нову копію індексу, а не оновлення існуючого та потребує розбиття сторінок.


9

Це безумовно заново створить не кластерний індекс, а не лише оновить метадані. Це тестується на SQL 2014 і дійсно не повинно тестуватися у виробничій системі:

CREATE TABLE [z](
    [a] [int] IDENTITY(1,1) NOT NULL,
    [b] [int] NOT NULL,
 CONSTRAINT [c_a] PRIMARY KEY CLUSTERED  ([a] ASC))
go
CREATE NONCLUSTERED INDEX [nc_b] on z (b asc)
GO
insert into z (b)
values (1);

А тепер для цікавої частини:

DBCC IND (0, z, -1)

Це дасть нам сторінки бази даних, де зберігаються таблиця та некластеризований індекс.

Знайдіть PagePIDде IndexID2 та PageType2, а потім виконайте наступне:

DBCC TRACEON(3604) --are you sure that you are allowed to do this?

і потім:

dbcc page (0, 1, PagePID, 3) with tableresults

Зауважте, що в заголовку є нульова растрова карта:

Витяг заголовка сторінки

Тепер зробимо:

alter table z alter Column b int null;

Якщо ви справді нетерплячі, ви можете спробувати запустити dbcc pageкоманду ще раз, але вона не вдасться, тому давайте ще раз перевіримо розподіл DBCC IND (0, z, -1). Сторінка буде переміщена ніби за допомогою магії.

Таким чином, зміна нульовості стовпця вплине на зберігання некластеризованих індексів, які охоплюють цей стовпець, оскільки метадані потрібно оновлювати, і вам не потрібно буде перебудовувати індекси згодом.


Багато ALTER TABLE ... ALTER COLUMN ...операцій можна виконати, ONLINEпочинаючи з SQL Server 2016, але:

ALTER TABLE (Transact-SQL)

  • Зміна стовпця з NOT NULLв NULLне підтримується як онлайн-операція, коли на змінений стовпець посилаються некластеризовані індекси.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.