2010-07-29 17 views
7

Estoy usando SQL Server 2005 Express. Quiero usar SMO para recorrer cada tabla en una base de datos y cambiar cada columna Char a una columna Varchar. Si una columna es miembro de la clave principal, primero debo soltar la clave primaria antes de alterar el tipo de datos de la columna. Entonces necesito recrear el índice. Aquí está el código que estoy tratando de usar:Cómo descartar y volver a crear y el índice de clave principal utilizando SMO con 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(); 
    } 
} 

Pero cuando intento para crear el índice me sale una excepción con el mensaje "No se puede acceder a propiedades y métodos para la Microsoft.SqlServer.Management.Smo.Index ' [PK_table1] ', porque se ha eliminado. " Entonces, ¿hay una buena manera de lograr lo que quiero hacer con SMO?

Intenté hacer una secuencia de comandos del índice antes de descartarlo utilizando el método de secuencia de comandos de Índice, pero arroja una excepción con el mensaje "El índice 'PK_table1' hace referencia a la columna inexistente '[mesa1]. [Propietario]'." La columna del dueño claramente existe.

+0

Si existe una columna y SQL dice que no, me gustaría ver los problemas de seguridad, ¿en qué contexto se ejecutan sus comandos? – Sam

+0

Los comandos se ejecutan bajo el inicio de sesión del administrador del sistema (sa). – YWE

+1

Es más complicado que lo sepas. No puede simplemente dejar caer la clave primaria si se hace referencia a ella en una relación de clave externa. Por lo tanto, para cada PK, deberá encontrar todas las referencias de FK, descartarlas, descartar PK, cambiar el tipo de datos y luego volver a agregarlos en orden inverso. – Thomas

Respuesta

0

Posiblemente el objeto Index ha perdido su referencia a la Tabla. ¿Ha intentado volver a agregar el índice al objeto de la tabla?

table.Indexes.Add(pk); 
+0

Desafortunadamente esto no funciona. Intenté la declaración anterior antes de "pk.Create()" y obtuve la misma excepción. Lo intenté y luego "table.Alter()" en lugar de Create y obtuve la misma excepción. – YWE

0

En lugar de caer y volver a crear el índice, simplemente tratar su desactivación y luego volver a habilitar cuando haya terminado de usar los métodos y .Disable.Enable.

+0

Esto no funciona. Después de deshabilitar el índice, alterar las columnas y llamar a "table.Alter()", recibo una excepción con el mensaje "No se puede realizar la operación especificada en la tabla 'table1' porque su índice agrupado 'PK_table1' está deshabilitado." – YWE

4

Intente crear la clave principal nuevamente de la siguiente manera.

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

Esto es lo que estaba pensando que tendría que terminar haciendo. Estaba tratando de evitar tener que replicar todas las diversas propiedades del índice. Lo que sería bueno es un tipo de función de clonación. – YWE

+0

Esto podría ser útil para crear perfiles en sqlexpress. http://stackoverflow.com/questions/47376/how-can-i-monitor-the-executed-sql-statements-on-a-ms-sql-server-2005/1210766#1210766 – IsmailS

5

que era capaz de soltar la clave principal, alterar los tipos de datos de columna, y volver a crear la clave principal con este código:

// 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); 
    }     
} 

Algunas advertencias:

  1. La línea que define pk arrojará una excepción si hay una tabla sin índices
  2. eliminación de la clave primaria fallará si el cuadro de referencia en una vista enlazada a esquema
  3. Dejar caer la clave principal fallará si la tabla es referenciado por restricciones de clave externa
  4. cambiar los datos tipo de la columna fallará si esa columna se utiliza en un índice no agrupado
  5. Si usted tiene una mesa muy grande, dejando caer una clave principal agrupada convertirá la tabla a un montón. El tiempo necesario para quitar el índice agrupado sugerirá el proceso ha fallado (mientras que, de hecho, que todavía se está ejecutando)
  6. Es de suponer que lo que se necesita código para vacía la StringCollection después de la secuencia de comandos índice fue ejecutado
+0

De nuevo cuando ejecuto el Método de script Obtengo una excepción FailedOperationException diciendo que "The Index 'PK_table1' hace referencia a la columna inexistente '[dbo]. [Table1]. [Owner]'". La columna obviamente existe. Puedo ir a SQL Server Management Studio, crear un script del índice desde allí, mirar el script y ver claramente que la columna [owner] está ahí. Puedo utilizar Management Studio para crear una nueva base de datos, una nueva tabla, luego intento hacer una secuencia de comandos con la clave primaria usando SMO y obtengo la misma excepción FailedOperationException. Todos usando el inicio de sesión sa – YWE

+0

¿Puedes publicar el esquema de la tabla? Pude ejecutar esto en un db pequeño con 2 tablas simples. – 8kb

+0

Otra idea: ¿ha intentado ejecutar Profiler contra la base de datos mientras ejecuta el código? Esto mostrará las sentencias t-SQL exactas ejecutadas desde el SMO. – 8kb

0

¿Tiene la opción de ejecutar scripts SQL a través de SMO? Empecé a ejecutar todos los scripts que hacen modificaciones estructurales de DB a través de ServerConnection.ExecuteNonQuery.Gran mejora de la estabilidad sobre la pila usual de SqlCommand etc. (tampoco se compara con GO-s :-) Terminó siendo una fusión muy útil. Conexión no MARS, por supuesto.

0

Me encontré con el mismo problema con SMO diciéndome que las columnas utilizadas en el índice no existían. Lo resolví llamando al método Discover() en el objeto Table antes de llamar a Script() en mi ForeignKey. Esto es diferente de Table.Refresh que no lee todos los metadatos de la tabla. Llamar a Table.Discover() ralentiza notoriamente el código, pero la llamada a ForeignKey.Script tiene éxito después de hacerlo. No tiene que guardar la Lista que Discover devuelve a menos que la necesite para algo. Pero forzar que los metadatos se obtengan de esta manera hace que la función de scripts funcione.

Cuestiones relacionadas