如何使用SMO与SQL Server删除并重新创建主键索引?

时间:2010-07-29 15:04:46

标签: c# .net sql-server-2005 smo

我正在使用SQL Server 2005 Express。我想使用SMO循环遍历数据库中的每个表,并将每个Char列更改为Varchar列。如果列是主键的成员,我需要先删除主键,然后再更改列的数据类型。然后我需要重新创建索引。这是我试图使用的代码:

foreach (Table table in database.Tables)
{
    Index pk = table.Indexes.Cast<Index>().SingleOrDefault(index => index.IndexKeyType == IndexKeyType.DriPrimaryKey);
    if (pk != null)
    {
        pk.Drop();
        table.Alter();
    }
    foreach (Column column in table.Columns.Cast<Column>().Where(column => column.DataType.SqlDataType == SqlDataType.Char))
    {
        column.DataType = new DataType(SqlDataType.VarChar, column.DataType.MaximumLength);
    }
    table.Alter();
    if (pk != null)
    {
        pk.Create();
    }
}

但是当我尝试创建索引时,我得到一个异常,消息“无法访问Microsoft.SqlServer.Management.Smo.Index'[PK_table1]'的属性或方法,因为它已被删除。”那么有没有一种方法可以完成我想用SMO做的事情?

我在使用Index的Script方法删除它之前尝试编写索引脚本,但它抛出了一个异常,消息“索引'PK_table1'引用了不存在的列'[table1]。[owner]'。”所有者专栏显然存在。

6 个答案:

答案 0 :(得分:5)

我能够删除主键,更改列数据类型,并使用以下代码重新创建主键:

// using System.Collections.Specialized;

foreach (Table table in database.Tables)
{
    // object to hold the index script
    StringCollection pk_script = new StringCollection();

    Index pk = table.Indexes.Cast<Index>().SingleOrDefault(index => index.IndexKeyType == IndexKeyType.DriPrimaryKey);
    if (pk != null)
    {
        // script the index
        pk_script = pk.Script();
        pk.Drop();
        table.Alter();
    }
    foreach (Column column in table.Columns.Cast<Column>().Where(column => column.DataType.SqlDataType == SqlDataType.Char))
    {
        column.DataType = new DataType(SqlDataType.VarChar, column.DataType.MaximumLength);
    }
    table.Alter();

    // iterate through script StringCollection
    foreach (String tsql in pk_script)
    {
        database.ExecuteNonQuery(tsql);
    }                
} 

一些警告:

  1. 定义pk的行将抛出 如果有表格则例外 没有索引
  2. 删除主键将失败 如果表是由架构绑定视图引用的
  3. 删除主键将失败 如果表被引用 外键约束
  4. 更改列的数据类型 如果使用该列将失败 非聚集索引
  5. 如果你有一张很大的桌子, 删除群集主键 将表转换为堆。 删除的时间 聚集索引会建议 过程失败了(事实上, 它还在运行)
  6. 大概你需要代码 之后清空StringCollection 索引脚本已执行

答案 1 :(得分:4)

尝试以下列方式再次创建主键。

Index index = new Index(table, "PK_tableNameTable");
index.IndexKeyType = IndexKeyType.DriPrimaryKey;

//You will have to store the names of columns before deleting the key.
index.IndexedColumns.Add(new IndexedColumn(index,"ID")); 

table.Indexes.Add(index);

Source of the snippet

答案 2 :(得分:0)

可能是Index对象丢失了它对Table的引用。 您是否尝试将索引添加回表对象?

table.Indexes.Add(pk);

答案 3 :(得分:0)

不要删除并重新创建索引,而是尝试禁用它,然后在使用.Disable.Enable方法完成后重新启用它。

答案 4 :(得分:0)

您是否可以选择通过SMO运行SQL脚本?我开始运行所有通过ServerConnection.ExecuteNonQuery进行任何结构数据库修改的脚本。与通常的SqlCommand等堆栈相比具有很大的稳定性改进(不兼容GO-s :-)结束了非常有用的融合。当然,非MARS连接。

答案 5 :(得分:0)

我遇到了与SMO相同的问题,告诉我索引中使用的列不存在。我通过在ForeignKey上调用Script()之前调用Table对象上的Discover()方法来解决它。这与Table.Refresh不同,后者没有读取所有表的元数据。调用Table.Discover()会显着减慢代码速度,但在执行此操作后,对ForeignKey.Script的调用会成功。您不必保存Discover返回的List,除非您需要它。但是强制以这种方式获取元数据确实使脚本功能起作用。