Есть ли способ изменить тип данных столбца как операцию только для метаданных?
Я так не думаю, вот как продукт работает прямо сейчас. В ответе Джо есть несколько действительно хороших способов обойти это ограничение .
... в результате SQL Server переписывает всю таблицу (и использует 2x размер таблицы в пространстве журнала)
Я собираюсь ответить на две части этого заявления отдельно.
Переписывая таблицу
Как я упоминал ранее, на самом деле нет никакого способа избежать этого. Кажется, что это реальность ситуации, даже если она не имеет полного смысла с нашей точки зрения как клиентов.
Просмотр DBCC PAGE
до и после изменения столбца с 4000 на 260 показывает, что все данные дублируются на странице данных (моя тестовая таблица имела 'A'
260 строк подряд):
На данный момент на странице есть две копии абсолютно одинаковых данных. «Старый» столбец по существу удаляется (идентификатор изменяется с id = 2 на id = 67108865), а «новая» версия столбца обновляется, чтобы указывать на новое смещение данных на странице:
Использование 2x размера таблицы в пространстве журнала
Добавление WITH (ONLINE = ON)
в конец ALTER
оператора сокращает активность журналирования примерно вдвое , поэтому это одно из улучшений, которое вы можете сделать, чтобы уменьшить количество операций записи на диск / дисковое пространство, необходимое.
Я использовал этот тестовый жгут, чтобы опробовать его:
USE [master];
GO
DROP DATABASE IF EXISTS [248749];
GO
CREATE DATABASE [248749]
ON PRIMARY
(
NAME = N'248749',
FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\248749.mdf',
SIZE = 2048000KB,
FILEGROWTH = 65536KB
)
LOG ON
(
NAME = N'248749_log',
FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\248749_log.ldf',
SIZE = 2048000KB,
FILEGROWTH = 65536KB
);
GO
USE [248749];
GO
CREATE TABLE dbo.[table]
(
id int IDENTITY(1,1) NOT NULL,
[col] nvarchar (4000) NULL,
CONSTRAINT [PK_test] PRIMARY KEY CLUSTERED (id ASC)
);
INSERT INTO dbo.[table]
SELECT TOP (1000000)
REPLICATE(N'A', 260)
FROM master.dbo.spt_values v1
CROSS JOIN master.dbo.spt_values v2
CROSS JOIN master.dbo.spt_values v3;
GO
Я проверил sys.dm_io_virtual_file_stats(DB_ID(N'248749'), DEFAULT)
до и после запуска ALTER
оператора, и вот различия:
По умолчанию (в автономном режиме) ALTER
- Запись в файл данных / записано байтов: 34 809/2 193 801 216
- Записывает в файл журнала / записано байтов: 40,953 / 1,484,910,080
В сети ALTER
- Запись в файл данных / записанных байтов: 36 874/1 693 745 152 (падение на 22,8%)
- Записывает в файл журнала / записано байтов: 24,680 / 866,166,272 (падение на 41%)
Как вы можете видеть, произошло небольшое снижение количества записей в файле данных и значительное падение в записи файла журнала.
update table set new_col = old_col where new_col <> old_col;
прежде чем броситьold_col
.where new_col <> old_col
не приведет ни к каким другим условиям фильтрации, вы можете добавить триггер для переноса этих изменений по мере их возникновения и удалить его в конце процесса. Все еще потенциальное снижение производительности, но много небольших по длине процесса вместо одного огромного попадания в конце, вероятно (в зависимости от шаблона обновления вашего приложения для таблицы) в сумме, в общем, намного меньше, чем тот огромный скачок ,Ну, есть альтернатива в зависимости от свободного места в вашей базе данных.
Создайте точную копию вашей таблицы (например
new_table
), за исключением столбца, где вы будете сокращать сNVARCHAR(4000)
доNVARCHAR(260)
:В окне обслуживания скопируйте данные из «сломанной» таблицы (
table
) в «фиксированную» таблицу (new_table
) с помощью простогоINSERT ... INTO ... SELECT ....
:Переименуйте «сломанную» таблицу
table
во что-то другое:Переименуйте «фиксированную» таблицу
new_table
вtable
:Если все в порядке, удалите «сломанную» переименованную таблицу:
Вот и ты.
Отвечая на ваши вопросы
Нет. В настоящее время невозможно
Нет.
( См. Мое решение и другие. )
источник