ALTER TABLE ... ALTER COLUMN
командата е много мощна. Можете да го използвате, за да промените типа данни на колоната, дължината, точността, мащаба, нулирането, съпоставянето... и много други неща освен това.
Със сигурност е по-удобно от алтернативата:Създаване на нова таблица и мигриране на данните всеки път, когато е необходима промяна. Въпреки това може да се направи толкова много, за да се скрие основната сложност. Наред с голям брой ограничения за това, което дори е възможно с тази команда, винаги съществува въпросът за производителността.
В крайна сметка таблиците се съхраняват като последователност от байтове с някои метаданни на друго място в системата, за да опишат какво означава всеки от тези байтове и как се отнасят към всяка от различните колони на таблицата. Когато помолим SQL Server да промени някакъв аспект от дефиницията на колона, той трябва да провери дали съществуващите данни са съвместими с новата дефиниция. Той също така трябва да определи дали текущото физическо оформление трябва да се промени.
В зависимост от вида на промяната и конфигурацията на базата данни, ALTER COLUMN
командата ще трябва да извърши едно от следните действия:
- Променете метаданните само в системните таблици.
- Проверете всички съществуващи данни за съвместимост, след което променете метаданните.
- Пренапишете някои или всички съхранени данни, за да съответстват на новата дефиниция.
Вариант 1 представлява идеалния случай от гледна точка на производителността. Изисква само няколко промени в системните таблици и минимално количество регистриране. Операцията все пак ще изисква ограничителна модификация на схемата Sch-M
заключете, но самите промени в метаданните ще завършат много бързо, независимо от размера на таблицата.
Промени само за метаданни
Има редица специални случаи, за които трябва да внимавате, но като общо обобщение, следните действия изискват промени само в метаданните:
- Преминаване от
NOT NULL
доNULL
за същия тип данни. - Увеличаване на максималния размер на
varchar
,nvarchar
, илиvarbinary
колона (с изключение наmax
).
Подобрения в SQL Server 2016
Темата на тази публикация са допълнителните промени, които са разрешени само за метаданни от SQL Server 2016 нататък . Не са необходими промени в синтаксиса и не е необходимо да се променят настройките за конфигурация. Получавате тези недокументирани подобрения безплатно.
Новите възможности са насочени към подмножество от фиксирана дължина типове данни. Новите способности се прилагат към таблици за съхранение на редове при следните обстоятелства:
- Компресията трябва да е активирана:
- На всички индекси и дялове , включително базовата купчина или клъстериран индекс.
- Или
ROW
илиPAGE
компресия. - Индексите и дяловете може да използват смес от тези нива на компресия. Важното е, че няма некомпресирани индекси или дялове.
- Промяна от
NULL
доNOT NULL
ене е позволено . - Следните промени в типа на цяло число се поддържат:
smallint
къмinteger
илиbigint
.integer
къмbigint
.smallmoney
къмmoney
(използва вътрешночислено представяне).
- Следните низови и двоични промени в типа се поддържат:
char(n)
къмchar(m)
илиvarchar(m)
nchar(n)
къмnchar(m)
илиnvarchar(m)
binary(n)
къмbinary(m)
илиvarbinary(m)
- Всичко по-горе само за
n < m
иm != max
- Промени в съпоставянето не са разрешени
Тези промени могат да бъдат само с метаданни, тъй като основното оформление на двоични данни не се променя, когато Дескриптор на колона използва се формат на ред (оттук и необходимостта от компресиране). Без компресия магазинът на редове използва оригиналния FixedVar представяне, което не може да побере тези промени на типа данни с фиксирана дължина, без да се пренапише физическото оформление.
Може да забележите, че tinyint
е пропуснат от списъка с целочислени типове. Това е така, защото е без знак, докато всички други типове цели числа са със знак, така че промяна само на метаданни не е възможна. Например, стойност от 255 може да се побере в един байт за tinyint
, но изисква два байта във всеки от подписаните формати. Подписаните формати могат да задържат от -128 до +127 в един байт, когато са компресирани.
Пример за цяло число
Едно много удобно приложение на това подобрение е промяна на типа данни на колона с IDENTITY
собственост.
Да кажем, че имаме следната таблица на купчина, използвайки компресия на редове (компресията на страница също би работила):
ИЗПУСКАНЕ НА ТАБЛИЦА, АКО СЪЩЕСТВУВА dbo.Test;GOCREATE TABLE dbo.Test( id integer IDENTITY NOT NULL, some_value integer NOT NULL)WITH (DATA_COMPRESSION =ROW);
Нека добавим 5 милиона реда данни. Това ще бъде достатъчно, за да стане очевидно (от гледна точка на производителността) дали промяната на типа данни на колоната е операция само с метаданни или не:
С Числа КАТО( ИЗБЕРЕТЕ n =ROW_NUMBER() НАД (ПОРЪЧКА ОТ @@SPID) ОТ sys.all_columns КАТО AC1 КРЪСТО ПРИСЪЕДИНЕТЕ sys.all_columns КАТО AC2 ПОРЪЧАЙТЕ ПО n ОТМЕСТВАНЕ 0 РЕДОВЕ ИЗВЛЕЧВАНЕ ПЪРВО 5 * 1000 * R INSERT dbo.Test WITH (TABLOCKX)( some_value)SELECT N.nFROM Numbers AS N;
След това ще зададем повторно IDENTITY
за да изглежда, че сме почти в точката на изчерпване на стойности, които ще се поберат в integer
:
DBCC CHECKIDENT( N'dbo.Test', RESEED, 2147483646);
Можем успешно да добавим още един ред:
INSERT dbo.Test (some_value)VALUES (123456);
Но опит за добавяне на още един ред:
INSERT dbo.Test (some_value)VALUES (7890);
Резултатът е съобщение за грешка:
Съобщение 8115, ниво 16, състояние 1, ред 1Грешка при аритметично препълване при преобразуване на IDENTITY в тип данни int.
Можем да поправим това, като преобразуваме колоната в bigint
:
ALTER TABLE dbo.TestALTER COLUMN id bigint NOT NULL;
Благодарение на подобренията в SQL Server 2016, тази команда променя само метаданни , и завършва незабавно. Предишният INSERT
операторът (този, който е довел грешката при аритметичното препълване) сега завършва успешно.
Тази нова способност не решава всички проблеми около промяната на типа на колона с IDENTITY
Имот. Все пак ще трябва да пускаме и пресъздаваме всички индекси в колоната, да пресъздаваме всички рефериращи външни ключове и т.н. Това е малко извън обхвата на тази публикация (въпреки че Аарон Бертран е писал за това и преди). Възможността да промените типа като операция само с метаданни със сигурност не боли. С внимателно планиране другите необходими стъпки могат да бъдат направени възможно най-ефективни, например като се използва минимално регистриран или ONLINE
операции.
Бъдете внимателни със синтаксиса
Уверете се, че винагите посочете NULL
или NOT NULL
при промяна на типове данни с ALTER COLUMN
. Да кажем например, че искаме също да променим типа данни на some_value
колона в нашата тестова таблица от integer NOT NULL
към bigint NOT NULL
.
Когато пишем командата, пропускаме NULL
или NOT NULL
квалификатор:
ALTER TABLE dbo.TestALTER COLUMN some_value bigint;
Тази команда завършва успешно като промяна само на метаданни, но също така премахва NOT NULL
ограничение. Колоната вече е bigint NULL
, което не е това, което възнамерявахме. Това поведение е документирано, но е лесно да се пренебрегне.
Може да се опитаме да поправим грешката си с:
ALTER TABLE dbo.TestALTER COLUMN some_value bigint NOT NULL;
Това ене промяна само на метаданни. Не ни е позволено да променяме от NULL
до NOT NULL
(вижте по-ранната таблица, ако имате нужда от освежаване на условията). SQL Server ще трябва да провери всички съществуващи стойности, за да се увери, че няма нулеви стойности. След това физически пренаписва всеки ред на масата. Освен че са бавни сами по себе си, тези действия генерират голяма част от регистрационните файлове на транзакциите, които могат да имат въздействащ ефект.
Като странична забележка, същата грешка не е възможна за колони с IDENTITY
Имот. Ако напишем ALTER COLUMN
израз без NULL
или NOT NULL
в този случай машината услужливо приема, че имаме предвид NOT NULL
тъй като свойството identity не е разрешено в колони с нула. Все пак е страхотна идея да не разчитате на това поведение.
Винаги посочвайте NULL
или NOT NULL
с ALTER COLUMN
.
Сравняване
Необходима е особено внимание, когато се променя колона с низове, която има съпоставяне, което не съответства на стандартното за базата данни.
Например, да речем, че имаме таблица със съпоставяне, чувствително към малки и големи букви (да приемем, че базата данни по подразбиране е различна):
ИЗПУСКАНЕ НА ТАБЛИЦАТА, АКО СЪЩЕСТВУВА dbo.Test2;GOCREATE TABLE dbo.Test2( id integer IDENTITY NOT NULL, some_string char(8) СЪБОРЯВАНЕ Latin1_General_100_CS_AS НЕ NULL)С (DATA_COMPRESSION> =ROW);Добавете 5 милиона реда данни:
С Числа КАТО( ИЗБЕРЕТЕ n =ROW_NUMBER() НАД (ПОРЪЧКА ОТ @@SPID) ОТ sys.all_columns КАТО AC1 КРЪСТО ПРИСЪЕДИНЕТЕ sys.all_columns КАТО AC2 ПОРЪЧАЙТЕ ПО n ОТМЕСТВАНЕ 0 РЕДОВЕ ИЗВЛЕЧВАНЕ ПЪРВО 5 * 1000 * R INSERT dbo.Test2 WITH (TABLOCKX)( some_string)SELECT CONVERT(char(8), N.n) COLLATE Latin1_General_100_CS_ASFROM Числа AS N;Удвоете дължината на колоната на низа, като използвате следната команда:
ALTER TABLE dbo.Test2ALTER COLUMN some_string char(16) NOT NULL;Не забравяйте да посочим
NOT NULL
, но забравих за сравнението по подразбиране. SQL Server предполага, че сме искали да променим съпоставянето на базата данни по подразбиране (Latin1_General_CI_AS
за моята тестова база данни). Промяната на съпоставянето не позволява на операцията да бъде само с метаданни и така операцията работи за няколко минути, генерирайки купища регистрационни файлове.Създайте отново таблицата и данните с помощта на предишния скрипт, след което опитайте
ALTER COLUMN
команда отново, но посочване на съществуващото съпоставяне, което не е по подразбиране като част от командата:ALTER TABLE dbo.Test2ALTER COLUMN some_string char(16) COLLATE Latin1_General_100_CS_AS NOT NULL;Промяната сега завършва незабавно, като операция само с метаданни. Както при
NULL
иNOT NULL
синтаксис, струва си да бъдете изрични, за да избегнете инциденти. Това е добър съвет като цяло, не само заALTER COLUMN
.Компресия
Моля, имайте предвид, че компресията трябва да бъде изрично посочена за всеки индекс и отделно за основната таблица, ако е купчина. Това е друг пример, при който използването на съкратен синтаксис или преки пътища може да предотврати желания резултат.
Например, следната таблица не посочва изрично компресиране нито за първичния ключ, нито за дефиницията на вграден индекс:
СЪЗДАДЕТЕ ТАБЛИЦА dbo.Test( id integer IDENTITY NOT NULL PRIMARY KEY, some_value integer NOT NULL INDEX [IX dbo.Test some_value])WITH (DATA_COMPRESSION =PAGE);
PRIMARY KEY
ще има присвоено име, по подразбиранеCLUSTERED
,и да бъдеPAGE
компресиран. Вграденият индекс ще бъдеNONCLUSTERED
и изобщо не е компресиран. Тази таблица няма да бъде активирана за нито една от новите оптимизации, тъй като не всички индекси и дялове са компресирани.Много по-добра и по-ясна дефиниция на таблицата би била:
СЪЗДАДЕТЕ ТАБЛИЦА dbo.Test( id integer IDENTITY NOT NULL ОГРАНИЧЕНИЕ [PK dbo.Test id] ПЪРВИЧЕН КЛУСТ, КЛУСТРИРАН С (DATA_COMPRESSION =PAGE), some_value integer НЕ НУЛ ИНДЕКС [IX dbo.Test some_value] R. ) );Тази таблица ще отговаря на условията за новите оптимизации, тъй като всички индекси и дялове са компресирани. Както беше отбелязано по-рано, смесването на типовете компресия е добре.
Има различни начини да напишете тази
CREATE TABLE
изявление по изричен начин, така че има елемент на лично предпочитание. Важният момент е винаги да бъдете изрични за това, което искате. Това се отнася за отделенCREATE INDEX
също и изявления.Разширени събития и флаг за проследяване
Има разширено събитие специално за новите метаданни
ALTER COLUMN
операции, поддържани в SQL Server 2016 нататък.Разширеното събитие е
compressed_alter_column_is_md_only
в Отстраняване на грешки канал. Неговите полета за събития саobject_id
,column_id
иis_md_only
(вярно/невярно).Това събитие показва само дали дадена операция е само с метаданни поради новите възможности на SQL Server 2016. Промените в колоните, които са били само с метаданни преди 2016 г., ще покажат
is_md_only = false
въпреки че все още е само с метаданни.Други разширени събития, полезни за проследяване на
ALTER COLUMN
операциите включватmetadata_ddl_alter_column
иalter_column_event
, и двете в Analytic канал.Ако трябва да деактивирате новите възможности на SQL Server 2016 по някаква причина може да се използва недокументиран глобален (или стартиращ) флаг за проследяване 3618. Този флаг за проследяване не е ефективен, когато се използва на ниво сесия. Няма начин да посочите флаг за проследяване на ниво заявка с
ALTER COLUMN
команда.Последни мисли
Възможността да промените някои типове данни с фиксирана дължина с цели данни с промяна само на метаданни е много добре дошло подобрение на продукта. Това изисква таблицата вече да е напълно компресирана, но това така или иначе става все по-често срещано нещо. Това е особено вярно, тъй като компресирането беше активирано във всички издания, започвайки със SQL Server 2016 Service Pack 1.
Колоните от типа низ с фиксирана дължина вероятно са много по-рядко срещани. Част от това може да се дължи на малко остарели съображения, като например използването на пространство. Когато са компресирани, колоните с низове с фиксирана дължина не съхраняват крайни празни места, което ги прави също толкова ефективни, колкото колоните с низове с променлива дължина от гледна точка на съхранение. Може да е досадно да се отрязват пространства за манипулиране или показване, но ако данните обикновено заемат по-голямата част от максималната дължина, типовете с фиксирана дължина могат да имат важни предимства, не на последно място по отношение на предоставянето на памет за неща като сортиране и хеширане.
Не всичко е добра новина с активирана компресия. Споменах по-рано, че SQL Server понякога може да извърши промяна само на метаданни, след като провери дали всички съществуващи стойности ще се преобразуват успешно в новия тип. Такъв е случаят, когато използвате
ALTER COLUMN
за промяна отinteger
къмsmallint
например. За съжаление, тези операции в момента не са само за метаданни за компресирани обекти.Потвърждения
Специални благодарности на Панайотис Антонопулос (главен софтуерен инженер) и Мирек Щайно (старши програмен мениджър) от продуктовия екип на SQL Server за тяхната помощ и насоки по време на проучването и писането на тази статия.
Нито една от подробностите, дадени в тази работа, не трябва да се разглежда като официална документация на Microsoft или изявления за продукти.