2010-07-29 42 views
7

我正在使用SQL Server 2005 Express。我想使用SMO循環訪問數據庫中的每個表並將每個Char列更改爲Varchar列。如果列是主鍵的成員,則需要在更改列的數據類型之前首先刪除主鍵。然後我需要重新創建索引。這裏是我嘗試使用代碼:如何使用SMO與SQL Server刪除並重新創建主鍵索引?

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做的事情的好方法?我使用索引的腳本方法刪除它之前,我試圖腳本索引,但它引發異常與消息「The Index'PK_table1'引用不存在的列'[table1]。[所有者]'」。業主欄明確存在。

+0

如果某列存在,而SQL表示它不存在,那麼我會查看安全問題 - 您的命令在哪些上下文下運行? – Sam 2010-07-29 16:48:13

+0

這些命令在系統管理員登錄(sa)下運行。 – YWE 2010-07-29 22:05:25

+1

你知道的更復雜。如果主鍵在外鍵關係中引用,則不能簡單地刪除主鍵。因此,對於每個PK,您需要找到所有FK引用,刪除這些引用,刪除PK,更改數據類型,然後按相反順序將它們添加回去。 – Thomas 2010-08-10 04:03:10

回答

0

可能是Index對象失去了對錶的引用。 您是否嘗試將索引添加回表對象?

table.Indexes.Add(pk); 
+0

不幸的是,這不起作用。我在「pk.Create()」之前嘗試了上述語句並獲得相同的異常。我試了一下,然後「table.Alter()」,而不是創建並獲得相同的異常。 – YWE 2010-07-30 13:41:41

0

而是刪除並重新創建索引,而是嘗試禁用它,然後重新啓用它,當您使用.Disable.Enable方法來完成。

+0

這不起作用。禁用索引後,更改列並調用「table.Alter()」,我得到一個異常,並顯示消息「無法在表'table1上執行指定的操作,因爲它的聚集索引'PK_table1'被禁用。 – YWE 2010-08-06 15:30:10

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

+0

這就是我想我最終會做的事情。我試圖避免不得不復制索引的所有各種屬性。有什麼更好的是一種克隆功能。 – YWE 2010-08-10 14:19:00

+0

這對於在sqlexpress上進行性能分析可能非常方便。 http://stackoverflow.com/questions/47376/how-can-i-monitor-the-executed-sql-statements-on-a-ms-sql-server-2005/1210766#1210766 – IsmailS 2010-08-12 07:10:43

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
+0

當我運行腳本方法我得到一個FailedOperationException,說「索引'PK_table1'引用不存在的列'[dbo]。[table1]。[owner]'」。該列顯然存在。我可以進入SQL Server Management Studio,從那裏編寫索引,查看腳本並清楚地看到[owner]列在那裏。我可以使用Management Studio創建一個新數據庫,一個新表,然後嘗試使用SMO編寫主鍵,並獲得與FailedOperationException相同的結果。全部使用sa登錄。 – YWE 2010-08-11 14:07:15

+0

你可以發表表格的模式嗎?我能用2個簡單的表格在一個小分貝上運行。 – 8kb 2010-08-11 14:49:34

+0

另一個想法:您是否曾嘗試在運行代碼時針對數據庫運行Profiler?這將顯示從SMO執行的確切t-SQL語句。 – 8kb 2010-08-11 15:14:28

0

您是否有通過SMO運行SQL腳本的選項?我開始運行所有通過ServerConnection.ExecuteNonQuery進行結構數據庫修改的腳本。與通常的SqlCommand等堆棧相比,穩定性有了很大的提高(不兼容GO-s :-)最終成爲一個非常有用的融合。非MARS連接當然。

0

我遇到了與SMO相同的問題,告訴我在索引中使用的列不存在。我通過在調用我的ForeignKey上的Script()之前調用Table對象上的Discover()方法來解決此問題。這與Table.Refresh不同,後者不讀取表的所有元數據。調用Table.Discover()會顯着減慢代碼的速度,但這樣做後調用ForeignKey.Script會成功。您不必保存發現返回的列表,除非您需要某些東西。但是強制元數據以這種方式獲取會使腳本功能發揮作用。

相關問題