2008-11-24 23 views
59

Tengo una columna con una restricción "DEFAULT". Me gustaría crear un script que elimine esa columna.Columna de caída de SQL Server 2005 con restricciones

El problema es que devuelve este error:

Msg 5074, Level 16, State 1, Line 1 
The object 'DF__PeriodSce__IsClo__4BCC3ABA' is dependent on column 'IsClosed'. 
Msg 4922, Level 16, State 9, Line 1 
ALTER TABLE DROP COLUMN IsClosed failed because one or more objects access this column. 

no pude encontrar una manera fácil de eliminar una columna y todas sus limitaciones asociadas (sólo encontraron grandes secuencias de comandos que se ven en la tabla del sistema .. . DEBE (!!) ser una "buena" manera de hacerlo.

Y como el nombre de la restricción DEFAULT ha sido generado aleatoriamente, no puedo soltarlo por su nombre.


actualización:
El tipo de restricción es "default".

Vi las soluciones que todos ustedes propusieron, pero las encuentro realmente "sucias" ... ¿No creen? No sé si es con Oracle o MySQL, pero es posible hacer algo como:

DROP COLUMN xxx CASCADE CONSTRAINTS 

pero ha recibido todas las limitaciones relacionadas ... O por lo menos, de manera automática las restricciones asignadas a esa columna (¡al menos restricciones de CHECK!)

¿No hay nada como eso en MSSQL?

Respuesta

19

Esta consulta encuentra restricciones predeterminadas para una tabla dada. Aint bastante, estoy de acuerdo:

select 
    col.name, 
    col.column_id, 
    col.default_object_id, 
    OBJECTPROPERTY(col.default_object_id, N'IsDefaultCnst') as is_defcnst, 
    dobj.name as def_name 
from sys.columns col 
    left outer join sys.objects dobj 
     on dobj.object_id = col.default_object_id and dobj.type = 'D' 
where col.object_id = object_id(N'dbo.test') 
and dobj.name is not null 

[EDIT] Actualizado por el comentario de Julien N

+1

Tu respuesta es correcta, pero no hace lo mismo que la mía. Obtiene las restricciones de una tabla específica, la mía obtiene todas las restricciones. Dos cosas: error de sintaxis (elimine la coma antes de) y debe agregar "y dobj.name no es nulo" en la cláusula WHERE para eliminar los resultados no relevantes. –

+1

+1, solo necesitaba esto. –

0

¿Qué quiere decir aleatorio generado? Puede buscar las restricciones en la columna específica en Management Studio o a través de la vista sys.tables y buscar cuáles son los nombres.

Luego, puede cambiar la secuencia de comandos para eliminar las restricciones antes de soltar la columna. ¿Qué tipo de restricción es esto? Si se trata de una restricción de clave externa, asegúrese de que hacerlo no perjudicará la integridad de los datos en su base de datos.

3

Puede obtener los nombres de las restricciones consultando las vistas del sistema information_schema.

select CONSTRAINT_NAME from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_NAME = '<tablename>' AND COLUMN_NAME = 'IsClosed' 
+2

Esto no vuelve restricciones predeterminadas, como se menciona a continuación – edosoft

1

Creo que eliminar explícitamente las restricciones antes de eliminar la columna es una solución "limpia". De esta manera, no abandones restricciones de las que no estés al tanto. Si la caída aún falla, usted sabe que quedan restricciones adicionales. Me gusta tener el control de exactamente lo que está sucediendo en mi base de datos.

Además, la creación de scripts de las caídas garantiza explícitamente la secuencia de comandos y, con suerte, los resultados se pueden repetir exactamente de la manera que usted desea.

+1

"no dejar caer las limitaciones que puede no ser consciente de la" - Estoy de acuerdo. Pero no por las restricciones que solo se aplican en la columna eliminada ... Supongo que tendré que solicitar a la base de datos que conozca el nombre de la restricción para poder soltarla ... –

5
> select CONSTRAINT_NAME from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE 
> WHERE TABLE_NAME = '<tablename>' AND COLUMN_NAME = 'IsClosed' 

No es la solución correcta como se explica aquí: http://msdn.microsoft.com/en-us/library/aa175912.aspx que:

Unfortunately, the name of the column default constraint isn't kept in the ANSI COLUMNS view, so you must go back to the system tables to find the name

La única manera que encontré para obtener el nombre de la restricción por defecto es esta solicitud:

select 
    t_obj.name    as TABLE_NAME 
    ,c_obj.name    as CONSTRAINT_NAME 
    ,col.name    as COLUMN_NAME 

from sysobjects c_obj 
join sysobjects t_obj on c_obj.parent_obj = t_obj.id 
join sysconstraints con on c_obj.id = con.constid 
join syscolumns col on t_obj.id = col.id 
      and con.colid = col.colid 
where 
    c_obj.xtype = 'D' 

¿Soy el único que lo encontró loco para no poder eliminar fácilmente una restricción que solo se refiere a las columnas que estoy intentando eliminar?
Necesito ejecutar una solicitud con 3 uniones solo para obtener el nombre ...

+0

Ver mi respuesta para un poco consulta más limpia – edosoft

+0

¡No, no estás loco! – briantyler

+1

Al igual que la división/concatenación de cadenas, esta es otra situación donde todos deben implementar su propia solución kludgy, cuando la funcionalidad debería haber venido con el propio SQL Server. – sayap

-1

Just Generate Scripts para la tabla. Ahí puede encontrar el nombre de todas las restricciones.

62

Aquí es un script que va a eliminar la columna junto con su restricción predeterminada. Reemplace MYTABLENAME y MYCOLUMNNAME apropiadamente.

declare @constraint_name sysname, @sql nvarchar(max) 

select @constraint_name = name 
from sys.default_constraints 
where parent_object_id = object_id('MYTABLENAME') 
AND type = 'D' 
AND parent_column_id = (
    select column_id 
    from sys.columns 
    where object_id = object_id('MYTABLENAME') 
    and name = 'MYCOLUMNNAME' 
    ) 

set @sql = N'alter table MYTABLENAME drop constraint ' + @constraint_name 
exec sp_executesql @sql 

alter table MYTABLENAME drop column MYCOLUMNNAME 

go 
+0

bueno, esto se parece a lo que estoy buscando. – CRice

+0

Necesitaba hacer 'modificar tabla MYTABLENAME drop column MYCOLUMNNAME' –

+0

@Bill ¡Gracias! Lo he arreglado –

14

Tal vez podría ayudar un poco más:

declare @tablename nvarchar(200) 
declare @colname nvarchar(200) 
declare @default sysname, @sql nvarchar(max) 

set @tablename = 'your table' 
set @colname = 'column to drop' 

select @default = name 
from sys.default_constraints 
where parent_object_id = object_id(@tablename) 
AND type = 'D' 
AND parent_column_id = (
    select column_id 
    from sys.columns 
    where object_id = object_id(@tablename) 
    and name = @colname 
    ) 

set @sql = N'alter table ' + @tablename + ' drop constraint ' + @default 
exec sp_executesql @sql 

set @sql = N'alter table ' + @tablename + ' drop column ' + @colname 
exec sp_executesql @sql 

solo necesita ajustar las variables @colname @tablename & dejar caer la columna.

+0

muy buena respuesta, junto con la respuesta de [@Jeremy Stein] (http://stackoverflow.com/a/1171895/662581) ayúdame a crear la mía para resolver mi problema. –

5

También creo que es una falla en el servidor SQL no tener una caída en cascada disponible. He trabajado mi manera de evitarlo mediante la consulta de las tablas del sistema de la misma manera que otras personas describen a continuación:

  • INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE sólo las listas de claves externas, claves primarias y restricciones únicas.
  • La única forma de buscar las restricciones predeterminadas es buscarlas en sys.default_constraints.
  • Lo que no se ha mencionado aquí aún, es que los índices también hacen que falle una columna, por lo que también debe soltar todos los índices que utilizan su columna antes de proceder a soltar una columna.

El guión resultante no es bastante, pero lo puse en un procedimiento almacenado para poder reutilizarlo:

CREATE PROCEDURE DropColumnCascading @tablename nvarchar(500), @columnname nvarchar(500) 
AS 

SELECT CONSTRAINT_NAME, 'C' AS type 
INTO #dependencies 
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_NAME = @tablename AND COLUMN_NAME = @columnname 

INSERT INTO #dependencies 
select d.name, 'C' 
from sys.default_constraints d 
join sys.columns c ON c.column_id = d.parent_column_id AND c.object_id = d.parent_object_id 
join sys.objects o ON o.object_id = d.parent_object_id 
WHERE o.name = @tablename AND c.name = @columnname 

INSERT INTO #dependencies 
SELECT i.name, 'I' 
FROM sys.indexes i 
JOIN sys.index_columns ic ON ic.index_id = i.index_id and ic.object_id=i.object_id 
JOIN sys.columns c ON c.column_id = ic.column_id and c.object_id=i.object_id 
JOIN sys.objects o ON o.object_id = i.object_id 
where o.name = @tableName AND i.type=2 AND c.name = @columnname AND is_unique_constraint = 0 

DECLARE @dep_name nvarchar(500) 
DECLARE @type nchar(1) 

DECLARE dep_cursor CURSOR 
FOR SELECT * FROM #dependencies 

OPEN dep_cursor 

FETCH NEXT FROM dep_cursor 
INTO @dep_name, @type; 

DECLARE @sql nvarchar(max) 

WHILE @@FETCH_STATUS = 0 
BEGIN 
    SET @sql = 
     CASE @type 
      WHEN 'C' THEN 'ALTER TABLE [' + @tablename + '] DROP CONSTRAINT [' + @dep_name + ']' 
      WHEN 'I' THEN 'DROP INDEX [' + @dep_name + '] ON dbo.[' + @tablename + ']' 
     END 
    print @sql 
    EXEC sp_executesql @sql 
    FETCH NEXT FROM dep_cursor 
    INTO @dep_name, @type; 
END 

DEALLOCATE dep_cursor 

DROP TABLE #dependencies 

SET @sql = 'ALTER TABLE [' + @tablename + '] DROP COLUMN [' + @columnname + ']' 

print @sql 
EXEC sp_executesql @sql 
0

Me acabo de encontrar con esto. Puede eliminar la columna con restricciones utilizando la vista de diseño MSSQL. Haga clic con el botón derecho en la columna que desea eliminar (con o sin restricciones) y podrá eliminar esto sin ningún problema. Ha ... Ya parecía estúpido.

+1

Sí, puede eliminarlo a través de Enterprise Manager. El objetivo era hacerlo por script. No tuve acceso directo a la instancia del servidor sql. –

1

Buscar el nombre del contraint o utilizar la vista de diseño de MSSQL no siempre es una opción. Actualmente quiero hacer un script para eliminar una columna con un contraint en él. Usar el nombre no es una opción ya que el nombre se genera y quiero usar el script en diferentes entornos Dev/Sys/Prod/etc. No es posible usar el nombre porque los nombres de los términos diferirán según el entorno. Probablemente tenga que mirar la tabla de sistemas, pero estoy de acuerdo en que debería haber una opción más fácil disponible.

3

La respuesta de pvolders fue justo lo que necesitaba pero no detectó las estadísticas que causaban y error. Este es el mismo código, menos la creación de un procedimiento almacenado, además de enumerar las estadísticas y soltarlas. Esto es lo mejor que se me ocurre, así que si hay una forma mejor de determinar qué estadísticas se deben eliminar, agregue.

DECLARE @tablename nvarchar(500), 
     @columnname nvarchar(500) 

SELECT @tablename = 'tblProject', 
     @columnname = 'CountyKey' 


SELECT CONSTRAINT_NAME, 'C' AS type 
INTO #dependencies 
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_NAME = @tablename AND COLUMN_NAME = @columnname 

INSERT INTO #dependencies 
select d.name, 'C' 
from sys.default_constraints d 
join sys.columns c ON c.column_id = d.parent_column_id AND c.object_id = d.parent_object_id 
join sys.objects o ON o.object_id = d.parent_object_id 
WHERE o.name = @tablename AND c.name = @columnname 

INSERT INTO #dependencies 
SELECT i.name, 'I' 
FROM sys.indexes i 
JOIN sys.index_columns ic ON ic.index_id = i.index_id and ic.object_id=i.object_id 
JOIN sys.columns c ON c.column_id = ic.column_id and c.object_id=i.object_id 
JOIN sys.objects o ON o.object_id = i.object_id 
where o.name = @tableName AND i.type=2 AND c.name = @columnname AND is_unique_constraint = 0 

INSERT INTO #dependencies 
SELECT s.NAME, 'S' 
FROM sys.stats AS s 
INNER JOIN sys.stats_columns AS sc 
    ON s.object_id = sc.object_id AND s.stats_id = sc.stats_id 
INNER JOIN sys.columns AS c 
    ON sc.object_id = c.object_id AND c.column_id = sc.column_id 
WHERE s.object_id = OBJECT_ID(@tableName) 
AND c.NAME = @columnname 
AND s.NAME LIKE '_dta_stat%' 

DECLARE @dep_name nvarchar(500) 
DECLARE @type nchar(1) 

DECLARE dep_cursor CURSOR 
FOR SELECT * FROM #dependencies 

OPEN dep_cursor 

FETCH NEXT FROM dep_cursor 
INTO @dep_name, @type; 

DECLARE @sql nvarchar(max) 

WHILE @@FETCH_STATUS = 0 
BEGIN 
    SET @sql = 
     CASE @type 
      WHEN 'C' THEN 'ALTER TABLE [' + @tablename + '] DROP CONSTRAINT [' + @dep_name + ']' 
      WHEN 'I' THEN 'DROP INDEX [' + @dep_name + '] ON dbo.[' + @tablename + ']' 
      WHEN 'S' THEN 'DROP STATISTICS [' + @tablename + '].[' + @dep_name + ']' 
     END 
    print @sql 
    EXEC sp_executesql @sql 
    FETCH NEXT FROM dep_cursor 
    INTO @dep_name, @type; 
END 

DEALLOCATE dep_cursor 

DROP TABLE #dependencies 

SET @sql = 'ALTER TABLE [' + @tablename + '] DROP COLUMN [' + @columnname + ']' 

print @sql 
EXEC sp_executesql @sql 
3

Sólo para construir sobre la respuesta de Jeremy Stein, he creado un procedimiento almacenado para esto, y configurarlo de modo que se puede utilizar para eliminar una columna que tiene o no tiene restricciones predeterminadas. No es muy eficiente, ya que está consultando sys.columns dos veces, pero funciona.

CREATE PROCEDURE [dbo].[RemoveColumnWithDefaultConstraints] 
    -- Add the parameters for the stored procedure here 
    @tableName nvarchar(max), 
    @columnName nvarchar(max) 
AS 
BEGIN 
    -- SET NOCOUNT ON added to prevent extra result sets from 
    -- interfering with SELECT statements. 
    SET NOCOUNT ON; 

    DECLARE @ConstraintName nvarchar(200) 
    SELECT @ConstraintName = Name 
    FROM SYS.DEFAULT_CONSTRAINTS 
    WHERE PARENT_OBJECT_ID = OBJECT_ID(@tableName) 
     AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns WHERE NAME = (@columnName) 
     AND object_id = OBJECT_ID(@tableName)) 
    IF @ConstraintName IS NOT NULL 
     EXEC('ALTER TABLE ' + @tableName + ' DROP CONSTRAINT ' + @ConstraintName) 

    IF EXISTS(SELECT * FROM sys.columns WHERE Name = @columnName 
     AND Object_ID = Object_ID(@tableName)) 
     EXEC('ALTER TABLE ' + @tableName + ' DROP COLUMN ' + @columnName)  
END 
GO 
+0

¡El enfoque sigiloso gana! – Bern

Cuestiones relacionadas