我的 SQL Server 2008 中有一个情况。
我需要更改列类型,但索引阻止了更改。但是由于数据库在多个客户端上,我不知道该列存在多少索引。
有没有办法以编程方式获取所有涉及列的索引并删除它们,并在alter table
语句之后自动重新创建它们?
我听说禁用它们会因为类型的变化而弄乱表格。
我正在从 tinyint 更改为 smallint 类型。
我的 SQL Server 2008 中有一个情况。
我需要更改列类型,但索引阻止了更改。但是由于数据库在多个客户端上,我不知道该列存在多少索引。
有没有办法以编程方式获取所有涉及列的索引并删除它们,并在alter table
语句之后自动重新创建它们?
我听说禁用它们会因为类型的变化而弄乱表格。
我正在从 tinyint 更改为 smallint 类型。
也可以试试这个来了解表上具有列名的所有索引:
SELECT OBJECT_SCHEMA_NAME(ind.object_id) AS SchemaName
, OBJECT_NAME(ind.object_id) AS ObjectName
, ind.name AS IndexName
, ind.is_primary_key AS IsPrimaryKey
, ind.is_unique AS IsUniqueIndex
, col.name AS ColumnName
, ic.is_included_column AS IsIncludedColumn
, ic.key_ordinal AS ColumnOrder
FROM sys.indexes ind
INNER JOIN sys.index_columns ic
ON ind.object_id = ic.object_id
AND ind.index_id = ic.index_id
INNER JOIN sys.columns col
ON ic.object_id = col.object_id
AND ic.column_id = col.column_id
INNER JOIN sys.tables t
ON ind.object_id = t.object_id
WHERE t.is_ms_shipped = 0
ORDER BY OBJECT_SCHEMA_NAME(ind.object_id) --SchemaName
, OBJECT_NAME(ind.object_id) --ObjectName
, ind.is_primary_key DESC
, ind.is_unique DESC
, ind.name --IndexName
, ic.key_ordinal
禁用目标表上的所有索引
ALTER INDEX Indexname ON Table DISABLE
然后更改列的数据类型
ALTER TABLE table
ALTER COLUMN columnname datatype
之后启用索引
ALTER INDEX Indexname ON Table REBUILD
您可以使用以下脚本返回指定表/列的索引名称和类型。:
DECLARE @tableName SYSNAME
DECLARE @columnName SYSNAME
SET @tableName = 'Products'
SET @columnName = 'Name'
SELECT IDX.name, IDX.type_desc, IndexedColumn
FROM sys.tables TBL
INNER JOIN sys.indexes IDX ON TBL.object_id = IDX.object_id
LEFT JOIN sys.filegroups FG ON IDX.data_space_id = FG.data_space_id
CROSS APPLY
( SELECT COLS.Name
FROM sys.index_columns IXCL
INNER JOIN sys.columns COLS
ON IXCL.object_id = COLS.object_id
AND IXCL.column_id = COLS.column_id
WHERE IDX.object_id = IXCL.object_id
AND IDX.index_id = IXCL.index_id
AND COLS.name = @columnName
AND IDX.object_id = OBJECT_ID(@tableName)
) Indexed (IndexedColumn)
WHERE TBL.object_id = OBJECT_ID(@tableName)
希望这可以帮助...
您可以使用内置工具来完成这项工作。在 SQL Server Management Studio 中,单击“工具”,然后单击“选项”
展开“SQL Server 对象资源管理器”集,然后在其中单击“脚本”。
向下滚动到右侧的“表格和视图选项”。
找到名为“Script Indexes”的记录并将其设置为“True”,然后单击“确定”。
当您在对象资源管理器中右键单击您的表时,您可以选择“脚本为...”,选择这些选项中的任何一个现在将编写索引以及表本身及其键的脚本。复制所需的脚本,或者根据您的需要运行整个脚本。
让我们假设基本情况(列不是任何约束的一部分,不是具有 XML 索引的 XML 列等),可以执行以下操作:
select (...) from
<sys.indexes + other sys schema views> FOR XML ...
下面是一些安全删除和重新创建索引的示例 SQL:
IF(select object_id from sys.indexes where [name] = 'IDX_RecordSubscription' and object_id = OBJECT_ID('[SystemSetup].[RecordSubscription]')) IS NOT NULL
BEGIN
DROP INDEX [SystemSetup].[RecordSubscription].IDX_RecordSubscription
END
GO
CREATE UNIQUE INDEX
IDX_RecordSubscription
ON
[SystemSetup].[RecordSubscription]
(
Subscriber ASC,
MenuItem ASC,
RecordPrimaryKeyGuid ASC
)
WITH
(
PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF
) ON [PRIMARY]
GO
这是一些将其抽出的 C# 代码:
protected override string GetCreateIndexScript(string uniquePart, string indexName, string fullTableName, string columnsPart)
{
return
$"IF(select object_id from sys.indexes where [name] = '{indexName}' and object_id = OBJECT_ID('{fullTableName}')) IS NOT NULL \r\n" +
"BEGIN \r\n" +
$" DROP INDEX {fullTableName}.{indexName} \r\n " +
"END\r\n\r\n" +
"GO\r\n\r\n" +
$"CREATE {uniquePart} INDEX\r\n" +
$"\t{indexName}\r\n" +
"ON\r\n" +
$"\t{fullTableName}\r\n" +
"\t(\r\n" +
$"\t\t{columnsPart}\r\n" +
"\t)\r\n" +
"\tWITH\r\n" +
"\t(\r\n" +
"\t\tPAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = OFF\r\n" +
"\t) ON [PRIMARY] ";
}
下面是一些 C#(可以转换为 SQL)来获取索引模式:
const string selectStatement = "select " +
" SCHEMAs.name + '.' + tabs.name as OBJECT_ID, " +
" ind.name as INDEX_NAME, " +
" indcol.index_id AS INDEX_ID, " +
" indcol.key_ordinal AS COLUMN_ORDINAL, " +
" col.name AS COLUMN_NAME, " +
" ind.is_unique " +
"from " +
" sys.indexes ind " +
"inner join " +
" sys.index_columns indcol " +
"on " +
" ind.object_id = indcol.object_id and " +
" ind.index_id = indcol.index_id " +
"inner join " +
" sys.columns col " +
"on " +
" col.object_id = indcol.object_id and " +
" col.column_id = indcol.column_id " +
"inner join " +
" sys.tables tabs " +
"on " +
" tabs.object_id = ind.object_id " +
"inner join " +
" sys.schemas schemas " +
"on " +
" tabs.schema_id = schemas.schema_id " +
"where " +
" ind.type =2 and" +
" tabs.name <> 'sysdiagrams' " +
"order by " +
" tabs.object_id, " +
" indcol.index_id, " +
" indcol.key_ordinal ";
return DatabaseAdapter.Fill(selectStatement, null, null, null);
因此,基本上,您在此处执行最后一段代码,遍历结果(索引和列)并为返回的每个索引调用 GetCreateIndexScript。然后,您可以安全地执行为删除和重新创建索引而创建的每个语句。
相同的方法可以用于 TSQL 或其他语言。
这是我想出的删除/创建/禁用/启用(重建)Microsoft SQL Server 索引的方法:
CREATE VIEW dbo.vw_INDEX_TEXT AS
SELECT
x.[owner_name],
x.[table_name],
x.[index_name],
/*
** DROP index
*/
drop_sql = 'DROP INDEX ' + x.[index_name] + ' ON ' + x.[owner_name] + '.' + x.[table_name] + ';',
/*
** CREATE index
*/
create_sql =
'CREATE NONCLUSTERED INDEX ' + x.[index_name] + '
ON ' + x.[owner_name] + '.' + x.[table_name] + '(' +
-- the common-delimited field list.
substring(
(SELECT
',' + [field_name]
FROM
(
SELECT
[owner_name] = SCHEMA_NAME(O.schema_id),
[table_name] = O.name,
[index_name] = I.name,
[field_name] = C.name,
O.type,
S.key_ordinal,
S.is_descending_key,
S.is_included_column
FROM
sys.all_objects O inner join sys.indexes I ON (O.object_id = I.object_id )
inner join sys.index_columns S ON (O.object_id = S.object_id and I.index_id=S.index_id)
inner join sys.columns C ON (O.object_id = C.object_id and S.column_id = C.column_id)
WHERE
I.index_id > 0
AND SCHEMA_NAME(O.schema_id) = x.[owner_name] -- N'dbo'
AND I.name = x.[index_name] -- the index name N'IDX_myindex'
AND O.name = x.[table_name] -- the base table name N'mytable'
AND O.type <> 'IT'
AND I.is_primary_key = 0 -- we are not creating primary keys
AND I.is_unique_constraint = 0 -- we are not creating unique constraints
AND (INDEXPROPERTY(I.object_id,I.name,'IsStatistics') <> 1)
AND (INDEXPROPERTY(I.object_id,I.name,'IsAutoStatistics') <> 1)
AND (INDEXPROPERTY(I.object_id,I.name,'IsHypothetical') <> 1)
) as f
FOR XML PATH('')
), 2, 8000) -- we trim the leading comma
-- end of field list
+ ')
WITH (
PAD_INDEX = OFF
);',
/*
** DISABLE index
*/
disable_sql = 'ALTER INDEX ' + x.[index_name] + ' ON ' + x.[owner_name] + '.' + x.[table_name] + ' DISABLE;',
/*
** ENABLE (REBUILD) index
*/
enable_sql = 'ALTER INDEX ' + x.[index_name] + ' ON ' + x.[owner_name] + '.' + x.[table_name] + ' REBUILD;'
FROM
(SELECT
[owner_name] = SCHEMA_NAME(O.schema_id),
[table_name] = O.name,
[index_name] = I.name
-- other interesting, but not used fields that might be useful for other index types:
-- O.type,I.index_id,I.is_unique,
-- prop= INDEXPROPERTY(I.object_id,I.name,'IsClustered'),
-- I.is_padded,
-- I.fill_factor,
-- I.ignore_dup_key,I.allow_row_locks,I.allow_page_locks,I.is_disabled,I.data_space_id
FROM
sys.all_objects O INNER JOIN sys.indexes I on O.object_id=I.object_id
WHERE
I.index_id>0
-- AND O.name = @target_table_name
AND O.type <> 'IT'
AND INDEXPROPERTY(I.object_id,I.name,'IsStatistics') <> 1
AND INDEXPROPERTY(I.object_id,I.name,'IsAutoStatistics') <> 1
AND INDEXPROPERTY(I.object_id,I.name,'IsHypothetical') <> 1
AND I.is_primary_key = 0
AND I.is_unique_constraint = 0
) as x
仅重新创建最基本的索引,但我留下了一些额外的字段,这些字段可用作根据需要创建更复杂索引的基础。
命令位于视图的每个字段中。使用exec实际执行这样的操作(输入您的表名@target_table_name
):
Declare @target_table_name as sysname = 'mytable';
Declare @sql_cmd as table(ID int identity, cmd varchar(max) );
Declare @this_cmd as varchar(max) = '';
Declare @ct as int = 0;
-- populate a temp table to store the results
INSERT INTO @sql_cmd(cmd)
SELECT
cmd = enable_sql
FROM
dbo.vw_INDEX_TEXT
WHERE
table_name = @target_table_name
-- the ID column will help us step though the rows one at a time
SELECT @ct = max(ID) FROM @sql_cmd
-- loop over all rows in the table, finding each individual command
While (@ct > 0) Begin
SELECT
@this_cmd = cmd
FROM
@sql_cmd
WHERE
id = @ct
select @this_cmd
-- un-comment this line to actually run the command:
-- exec (@this_cmd)
SET @ct = @ct - 1
End
请注意,exec
在示例中禁用。