【问题标题】:How to drop and recreate and primary key index using SMO with SQL Server?如何使用 SMO 和 SQL Server 删除和重新创建主键索引?
【发布时间】:2011-03-22 19:02:18
【问题描述】:

我正在使用 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]'”。所有者列显然存在。

【问题讨论】:

  • 如果某个列存在而 SQL 说它不存在,我会考虑安全问题 - 您的命令在什么上下文环境下运行?
  • 命令在系统管理员登录(sa)下运行。
  • 你知道的更复杂。如果在外键关系中引用了主键,则不能简单地删除它。因此,对于每个 PK,您需要找到所有 FK 引用,删除这些引用,删除 PK,更改数据类型,然后以相反的顺序添加它们。
  • @Thomas。那是真实的。但是这个数据库中没有外键。

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


【解决方案1】:

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

// 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 索引脚本已执行

【讨论】:

  • 当我再次运行脚本方法时,我得到一个 FailedOperationException 说“索引 'PK_table1' 引用了不存在的列 '[dbo].[table1].[owner]'”。该列显然存在。我可以进入 SQL Server Management Studio,从那里编写索引脚本,查看脚本并清楚地看到 [owner] 列在其中。我可以使用 Management Studio 创建一个新数据库、一个新表,然后尝试使用 SMO 在其上编写主键脚本,我得到了相同的 FailedOperationException。全部使用sa登录。
  • 你能发布表格的架构吗?我能够在一个带有 2 个简单表的小型数据库上运行它。
  • 另一个想法:您是否尝试在运行代码时针对数据库运行 Profiler?这将显示从 SMO 执行的确切 t-SQL 语句。
  • 我现在意识到我忽略了我使用的是 SQL Server 2005 的 Express 版本。不幸的是,Profiler 没有附带它。
  • 这里也一样。这需要做更多的工作,但您可以使用服务器端跟踪(分析器本质上是一个 GUI)来做同样的事情:tinyurl.com/p6orqs
【解决方案2】:

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

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

【讨论】:

  • 这就是我认为我最终必须要做的事情。尽管必须复制索引的所有各种属性,但我试图避免。最好是一种克隆功能。
  • 这对于在 sqlexpress 上进行分析可能很方便。 stackoverflow.com/questions/47376/…
【解决方案3】:

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

table.Indexes.Add(pk);

【讨论】:

  • 不幸的是,这不起作用。我在“pk.Create()”之前尝试了上面的语句并得到了同样的异常。我试过了,然后用“table.Alter()”代替了创建,得到了同样的异常。
【解决方案4】:

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

【讨论】:

  • 这不起作用。在禁用索引、更改列并调用“table.Alter()”后,我收到一条异常消息“无法对表 'table1' 执行指定的操作,因为它的聚集索引 'PK_table1' 已禁用。”
【解决方案5】:

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

【讨论】:

    【解决方案6】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-04
      • 1970-01-01
      • 2020-09-01
      • 2020-12-11
      • 1970-01-01
      相关资源
      最近更新 更多