У меня есть столбец smalldatetime, который мне нужно изменить, чтобы быть столбцом datetime. Это то, что будет частью процесса установки, поэтому это не может быть ручной процедурой. К сожалению, столбец имеет несколько индексов и не имеет нулевого ограничения. Индексы связаны с производительностью и должны быть сохранены только с использованием нового типа данных. Можно ли написать выражение, которое позволит мне сохранить соответствующую информацию, все еще изменяя тип столбца? Если да, то как это можно сделать?
Сохранять индексы SQL при изменении типа столбца
Ответ 1
Вы не можете изменить тип данных из smalldatetime в datetime с индексами, уникальными ограничениями, ограничениями внешнего ключа или проверить ограничения на месте. Перед изменением типа вам придется отбросить их все. Тогда:
alter table T alter column TestDate datetime not null
Затем заново создайте ограничения и индексы, которые все еще применяются.
Некоторые различные подходы к генерации drop и создают:
1) Если вы указали явные имена для всех индексов и ограничений, ваш установщик может запускать статический script в каждой среде (dev, test, тестирование приёма пользователей, тестирование производительности и т.д.).
Чтобы создать этот явный script, вы можете: a) Используйте SSMS (или с SQL Server 2000, корпоративный менеджер) для script операторов создания и удаления. b) Работайте у вас в репозитории исходного кода, чтобы узнать имена и определения зависимых объектов и соединить соответствующий статический script. c) Попытайтесь запустить инструкцию alter. Посмотрите, что он терпит неудачу. Посмотрите определения и вручную напишите капли и создайте. (Лично это было бы здорово для написания капли, не так хорошо при создании.)
2) Если вы не указали явные имена для всех индексов и ограничений, то ваш установщик должен будет запросить словарь данных для соответствующих имен и использовать динамический SQL для запуска капель в правильном порядке до изменения столбца, а затем создает в правильном порядке после столбца alter.
Это будет проще, если вы знаете, что нет ограничений и просто индексов.
Могут быть инструменты или библиотеки, которые уже знают, как это сделать.
Кроме того, если это пакетное приложение, вы не можете быть уверены, что локальные администраторы баз данных не добавили индексы.
ПРИМЕЧАНИЕ. Если существует уникальное ограничение, он будет иметь индекс, который вы не сможете удалить с помощью DROP INDEX.
Ответ 2
Если вы просто изменяете размер, индекс все равно останется в таблице.
Если вы меняете тип данных, вы получите сообщение об ошибке, указывающее, что объекты зависят от столбца, который вы пытаетесь изменить, и поэтому вы не сможете его изменить.
Вы можете script вывести указанные индексы вручную или через script. В SSMS щелкните правой кнопкой мыши таблицу и script на объект, о котором идет речь.
Если вам нужен программный индексный скриптинг, вот хранимый процесс, который я использовал, который я получил от моего бывшего коллеги.
Drop Proc ScriptIndex
GO
Create Proc ScriptIndex
@TableName VarChar (Max),
@IndexScript VarChar (Max) OUTPUT
AS
-- Get all existing indexes, EXCEPT the primary keys
DECLARE cIX CURSOR FOR
SELECT OBJECT_NAME(SI.Object_ID), SI.Object_ID, SI.Name, SI.Index_ID
FROM Sys.Indexes SI
LEFT JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS TC
ON SI.Name = TC.CONSTRAINT_NAME
AND OBJECT_NAME(SI.Object_ID) = TC.TABLE_NAME
WHERE 1=1
AND OBJECT_NAME(SI.Object_ID) = @TableName
AND TC.CONSTRAINT_NAME IS NULL
AND OBJECTPROPERTY(SI.Object_ID, 'IsUserTable') = 1
ORDER BY OBJECT_NAME(SI.Object_ID), SI.Index_ID
DECLARE @IxTable SYSNAME
DECLARE @IxTableID INT
DECLARE @IxName SYSNAME
DECLARE @IxID INT
-- Loop through all indexes
OPEN cIX
FETCH NEXT FROM cIX INTO @IxTable, @IxTableID, @IxName, @IxID
WHILE (@@FETCH_STATUS = 0)
BEGIN
DECLARE @IXSQL NVARCHAR(4000)
DECLARE @PKSQL NVARCHAR(4000)
SET @PKSQL = ''
SET @IXSQL = 'CREATE '
-- Check if the index is unique
IF (INDEXPROPERTY(@IxTableID, @IxName, 'IsUnique') = 1)
SET @IXSQL = @IXSQL + 'UNIQUE '
-- Check if the index is clustered
IF (INDEXPROPERTY(@IxTableID, @IxName, 'IsClustered') = 1)
SET @IXSQL = @IXSQL + 'CLUSTERED '
SET @IXSQL = @IXSQL + 'INDEX ' + @IxName + ' ON ' + @IxTable + '('
-- Get all columns of the index
DECLARE cIxColumn CURSOR FOR
SELECT SC.Name
FROM Sys.Index_Columns IC
JOIN Sys.Columns SC ON IC.Object_ID = SC.Object_ID AND IC.Column_ID = SC.Column_ID
WHERE IC.Object_ID = @IxTableID AND Index_ID = @IxID
ORDER BY IC.Index_Column_ID
DECLARE @IxColumn SYSNAME
DECLARE @IxFirstColumn BIT SET @IxFirstColumn = 1
-- Loop throug all columns of the index and append them to the CREATE statement
OPEN cIxColumn
FETCH NEXT FROM cIxColumn INTO @IxColumn
WHILE (@@FETCH_STATUS = 0)
BEGIN
IF (@IxFirstColumn = 1)
SET @IxFirstColumn = 0
ELSE
SET @IXSQL = @IXSQL + ', '
SET @IXSQL = @IXSQL + @IxColumn
FETCH NEXT FROM cIxColumn INTO @IxColumn
END
CLOSE cIxColumn
DEALLOCATE cIxColumn
SET @IXSQL = @IXSQL + ')'
-- Print out the CREATE statement for the index
PRINT @IXSQL
FETCH NEXT FROM cIX INTO @IxTable, @IxTableID, @IxName, @IxID
END
CLOSE cIX
DEALLOCATE cIX
GO
Declare @TableName VarChar (Max), @IndexScript VarChar (Max)
Exec ScriptIndex 'Client', @IndexScript OUTPUT
Print @IndexScript
Ответ 3
EDIT. Это зависит от исходного и измененного типа данных. Если вы попытаетесь изменить столбец с varchar на nvarchar, он не удастся. Принимая во внимание, что если вы измените столбец с varchar (16) на varchar (32), это будет успешным.
--Disable Index
ALTER INDEX MyIndex ON MyTable DISABLE
GO
-- Change column datatype
--Enable Index
ALTER INDEX MyIndex ON MyTable REBUILD
GO
Если вы измените тип столбца, все индексы, которые используют этот столбец, должны быть перестроены.
Но если у вас нет огромных объемов данных (или работает 24/7), восстановление индексов не имеет большого значения. Просто запланировать окно обслуживания.
Ответ 4
Лучше всего создать процедуру, которая возвращает индекс script данной таблицы/столбца. Таким образом, вы можете удалить индексы только из измененного столбца, а не всех индексов из таблицы, тогда как создание индексов может быть несколько дорогим.
- Сохраняет результат процедуры в datatable
- Удалить индексы столбца
- Изменить колонку
-
Восстановить индексы, хранящиеся в datatable
-- objective : Generates indices scripting using specified column -- Parameters : -- @Tabela -> Name of the table that the column belongs to -- @Coluna -> Name of the column that will be searched for the indices to generate the script --Use: proc_ScriptIndexColumn 'TableName', 'CollumnName' SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO Create Proc proc_ScriptIndexColumn (@Tabela VARCHAR(4000), @Coluna VARCHAR(4000)) AS BEGIN DECLARE @isql_key VARCHAR(4000), @isql_incl VARCHAR(4000), @tableid INT, @indexid INT DECLARE @tablename VARCHAR(4000), @indexname VARCHAR(4000) DECLARE @isunique INT, @isclustered INT, @indexfillfactor INT DECLARE @srsql VARCHAR(MAX) DECLARE @ScriptsRetorno TABLE (Script VARCHAR(MAX)) DECLARE index_cursor CURSOR FOR SELECT tablename = OBJECT_NAME(i.[object_id]), tableid = i.[object_id], indexid = i.index_id, indexname = i.name, isunique = i.is_unique, CASE I.type_desc WHEN 'CLUSTERED' THEN 1 ELSE 0 END AS isclustered, indexfillfactor = i.fill_factor FROM sys.indexes AS i INNER JOIN SYSOBJECTS AS O ON I.[object_id] = O.ID INNER JOIN sys.index_columns AS ic ON (ic.column_id > 0 AND (ic.key_ordinal > 0 OR ic.partition_ordinal = 0 OR ic.is_included_column != 0 )) AND ( ic.index_id = CAST(i.index_id AS INT) AND ic.object_id = i.[object_id] ) INNER JOIN sys.columns AS sc ON sc.object_id = ic.object_id AND sc.column_id = ic.column_id WHERE O.XTYPE = 'U' AND i.typE = 2 /*Non clustered*/ AND i.is_unique = 0 AND i.is_hypothetical = 0 AND UPPER(OBJECT_NAME(i.[object_id])) = UPPER(@Tabela) AND UPPER(sc.name) = UPPER(@Coluna) OPEN index_cursor FETCH NEXT FROM index_cursor INTO @tablename,@tableid, @indexid,@indexname , @isunique ,@isclustered , @indexfillfactor WHILE @@fetch_status <> -1 BEGIN SELECT @isql_key = '', @isql_incl = '' SELECT @isql_key = CASE ic.is_included_column WHEN 0 THEN CASE ic.is_descending_key WHEN 1 THEN @isql_key +COALESCE(sc.name, '') + ' DESC, ' ELSE @isql_key + COALESCE(sc.name, '') + ' ASC, ' END ELSE @isql_key END, --include column @isql_incl = CASE ic.is_included_column WHEN 1 THEN CASE ic.is_descending_key WHEN 1 THEN @isql_incl + COALESCE(sc.name, '') + ', ' ELSE @isql_incl + COALESCE(sc.name, '') + ', ' END ELSE @isql_incl END FROM sysindexes i INNER JOIN sys.index_columns AS ic ON ( ic.column_id > 0 AND ( ic.key_ordinal > 0 OR ic.partition_ordinal = 0 OR ic.is_included_column != 0 ) ) AND (ic.index_id = CAST(i.indid AS INT) AND ic.object_id = i.id) INNER JOIN sys.columns AS sc ON sc.object_id = ic.object_id AND sc.column_id = ic.column_id WHERE i.indid > 0 AND i.indid < 255 AND (i.status & 64) = 0 AND i.id = @tableid AND i.indid = @indexid ORDER BY i.name, CASE ic.is_included_column WHEN 1 THEN ic.index_column_id ELSE ic.key_ordinal END IF LEN(@isql_key) > 1 SET @isql_key = LEFT(@isql_key, LEN(@isql_key) -1) IF LEN(@isql_incl) > 1 SET @isql_incl = LEFT(@isql_incl, LEN(@isql_incl) -1) SET @srsql = 'CREATE ' + 'INDEX [' + @indexname + ']' + ' ON [' + @tablename + '] ' SET @srsql = @srsql + '(' + @isql_key + ')' IF (@isql_incl <> '') SET @srsql = @srsql + ' INCLUDE(' + @isql_incl + ')' IF (@indexfillfactor <> 0) SET @srsql = @srsql + ' WITH ( FILLFACTOR = ' + CONVERT(VARCHAR(10), @indexfillfactor) + ')' FETCH NEXT FROM index_cursor INTO @tablename,@tableid,@indexid,@indexname, @isunique ,@isclustered , @indexfillfactor INSERT INTO @ScriptsRetorno VALUES (@srsql) END CLOSE index_cursor DEALLOCATE index_cursor SELECT * FROM @ScriptsRetorno RETURN @@ERROR END