Como soltar coluna com restrição?

138

Como descartar uma coluna que está com restrição padrão no SQL Server 2008?

Minha consulta é

alter table tbloffers
drop column checkin

Estou ficando abaixo do erro

O check-in ALTER TABLE DROP COLUMN falhou porque um ou mais objetos acessam essa coluna.

Alguém pode corrigir minha consulta para soltar uma coluna com restrição?

Robin Michael Poothurai
fonte
pode haver algumas referências a esta tabela de outras tabelas que estão causando esse erro.
Pankaj Upadhyay
Para os novatos que se deparam com isso, confira minha resposta abaixo : se funciona para você, é muito mais simples do que algumas das outras soluções.
BrainSlugs83
Eu listei minha pergunta e resposta aqui
Akash Yellappa

Respostas:

233

Primeiro você deve soltar a problemática DEFAULT constraint, depois você pode soltar a coluna

alter table tbloffers drop constraint [ConstraintName]
go

alter table tbloffers drop column checkin

Mas o erro pode aparecer por outros motivos - por exemplo, a função definida pelo usuário ou a visualização com a SCHEMABINDINGopção definida para eles.

UPD: Script de eliminação de restrições completamente automatizado:

DECLARE @sql NVARCHAR(MAX)
WHILE 1=1
BEGIN
    SELECT TOP 1 @sql = N'alter table tbloffers drop constraint ['+dc.NAME+N']'
    from sys.default_constraints dc
    JOIN sys.columns c
        ON c.default_object_id = dc.object_id
    WHERE 
        dc.parent_object_id = OBJECT_ID('tbloffers')
    AND c.name = N'checkin'
    IF @@ROWCOUNT = 0 BREAK
    EXEC (@sql)
END
Oleg Dok
fonte
1
Obrigado pelo script automatizado. Funciona como um encanto!
kanadianDri3
Muito obrigado - você me salvou muito tempo. Eu
vinculei
130

Aqui está outra maneira de eliminar uma restrição padrão com um nome desconhecido sem precisar primeiro executar uma consulta separada para obter o nome da restrição:

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 = N'__ColumnName__'
                        AND object_id = OBJECT_ID(N'__TableName__'))
IF @ConstraintName IS NOT NULL
EXEC('ALTER TABLE __TableName__ DROP CONSTRAINT ' + @ConstraintName)
Chris Halcrow
fonte
Uma excelente resposta, obrigado. Também votei na resposta acima, apenas com esse velho hábito de SELECIONAR e inspecionar antes de decidir abandonar.
noogrub
7
Ótima resposta, de fato. Fiz um procedimento armazenado para conveniência / uso futuro: pastebin.com/2CeXZDh2
Digs
Resposta excelente, mas ainda falta abordagem quando há mais de uma restrição vinculada a uma coluna. Alguns proc armazenado semelhante ao pós @Digs 'com loop incluído poderia ser 5 estrelas responder
YeinCM-QVA
27

Você também pode soltar a coluna e suas restrições em uma única instrução, em vez de individualmente.

CREATE TABLE #T
  (
     Col1 INT CONSTRAINT UQ UNIQUE CONSTRAINT CK CHECK (Col1 > 5),
     Col2 INT
  )

ALTER TABLE #T DROP CONSTRAINT UQ , 
                    CONSTRAINT CK, 
                    COLUMN Col1


DROP TABLE #T 

Algum SQL dinâmico que procurará os nomes de restrições de verificação dependentes e restrições padrão e os descartará junto com a coluna abaixo

(mas não outras dependências possíveis da coluna, como chaves estrangeiras, restrições de chave única e primária, colunas computadas, índices)

CREATE TABLE [dbo].[TestTable]
(
A INT DEFAULT '1' CHECK (A=1),
B INT,
CHECK (A > B)
)

GO

DECLARE @TwoPartTableNameQuoted nvarchar(500) = '[dbo].[TestTable]',
        @ColumnNameUnQuoted sysname = 'A',
        @DynSQL NVARCHAR(MAX);

SELECT @DynSQL =
     'ALTER TABLE ' + @TwoPartTableNameQuoted + ' DROP' + 
      ISNULL(' CONSTRAINT ' + QUOTENAME(OBJECT_NAME(c.default_object_id)) + ',','') + 
      ISNULL(check_constraints,'') + 
      '  COLUMN ' + QUOTENAME(@ColumnNameUnQuoted)
FROM   sys.columns c
       CROSS APPLY (SELECT ' CONSTRAINT ' + QUOTENAME(OBJECT_NAME(referencing_id)) + ','
                    FROM   sys.sql_expression_dependencies
                    WHERE  referenced_id = c.object_id
                           AND referenced_minor_id = c.column_id
                           AND OBJECTPROPERTYEX(referencing_id, 'BaseType') = 'C'
                    FOR XML PATH('')) ck(check_constraints)
WHERE  c.object_id = object_id(@TwoPartTableNameQuoted)
       AND c.name = @ColumnNameUnQuoted;

PRINT @DynSQL;
EXEC (@DynSQL); 
Martin Smith
fonte
Isso requer que você saiba o nome da restrição, no entanto. Se eles não tiverem sido nomeados durante a criação da tabela, obterão um nome gerado automaticamente.
Joey
1
@ Joey - Não há sintaxe para eliminar restrições sem saber o nome. É um argumento necessário para DROP CONSTRAINT ver a gramática. Se você não nomear explicitamente as restrições, deverá procurar o nome gerado pelo SQL Server, por exemplo, conforme a resposta de marc. Mas, depois de descobrir isso, você ainda pode eliminar a restrição e a coluna ao mesmo tempo.
Martin Smith
Bom código, eu precisava eliminar várias restrições de uma só vez, mas não a coluna. Seu alter fez o truque. Obrigado!!
Htm11h 12/07/2013
26

Encontre a restrição padrão com esta consulta aqui:

SELECT
    df.name 'Constraint Name' ,
    t.name 'Table Name',
    c.NAME 'Column Name'
FROM sys.default_constraints df
INNER JOIN sys.tables t ON df.parent_object_id = t.object_id
INNER JOIN sys.columns c ON df.parent_object_id = c.object_id AND df.parent_column_id = c.column_id

Isso fornece o nome da restrição padrão, bem como o nome da tabela e da coluna.

Quando você tem essas informações, primeiro descarte a restrição padrão:

ALTER TABLE dbo.YourTable
DROP CONSTRAINT name-of-the-default-constraint-here

e então você pode soltar a coluna

ALTER TABLE dbo.YourTable DROP COLUMN YourColumn
marc_s
fonte
2
Não precisa ser feito seqüencialmente. Você pode fazer os dois ao mesmo tempo.
Martin Smith
1
@ MartinSmith: OK, ótimo - obrigado por compartilhar! Eu não estava ciente dessa possibilidade - você aprende algo novo todos os dias! :-)
marc_s
Alguém pode fornecer um exemplo de como combinar essas duas declarações. Preciso de um pensamento do tipo: ALTER TABLE table DROP CONSTRAINT DF_XY DROP COLUMN XYInfelizmente, a sintaxe desta afirmação não está correta #
My-Name-Is
1
@ Meu-nome-é: se você verificar a resposta de Martin, você precisa colocar uma vírgula entre os dois DROPcomandos para fazer este trabalho
marc_s
3

O seguinte funcionou para mim em um back-end do SQL Azure (usando o SQL Server Management Studio), então YMMV, mas, se funcionar para você, é muito mais simples que as outras soluções.

ALTER TABLE MyTable
    DROP CONSTRAINT FK_MyColumn
    CONSTRAINT DK_MyColumn
    -- etc...
    COLUMN MyColumn
GO
BrainSlugs83
fonte
1

Eu tenho o mesmo:

ALTER TABLE DROP COLUMN falhou porque um ou mais objetos acessam esta mensagem da coluna .

Minha coluna tinha um índice que precisava ser excluído primeiro. O uso do sys.indexes fez o truque:

DECLARE @sql VARCHAR(max)

SELECT @sql = 'DROP INDEX ' + idx.NAME + ' ON tblName'
FROM sys.indexes idx
INNER JOIN sys.tables tbl ON idx.object_id = tbl.object_id
INNER JOIN sys.index_columns idxCol ON idx.index_id = idxCol.index_id
INNER JOIN sys.columns col ON idxCol.column_id = col.column_id
WHERE idx.type <> 0
    AND tbl.NAME = 'tblName'
    AND col.NAME = 'colName'

EXEC sp_executeSql @sql
GO

ALTER TABLE tblName
DROP COLUMN colName
Ewald Stieger
fonte
0

Atualizei o script um pouco para a minha versão do SQL Server

DECLARE @sql nvarchar(max)

SELECT @sql = 'ALTER TABLE `table_name` DROP CONSTRAINT ' + df.NAME 
FROM sys.default_constraints df
  INNER JOIN sys.tables t ON df.parent_object_id = t.object_id
  INNER JOIN sys.columns c ON df.parent_object_id = c.object_id AND df.parent_column_id = c.column_id
where t.name = 'table_name' and c.name = 'column_name'

EXEC sp_executeSql @sql
GO

ALTER TABLE table_name
  DROP COLUMN column_name;
Łukasz Dawid Wątor
fonte
0

Nem sempre é apenas uma restrição padrão que impede a queda de uma coluna e, às vezes, os índices também podem impedir que você a solte. Então, eu escrevi um procedimento que descarta qualquer índice ou restrição em uma coluna e a coluna no final.

IF OBJECT_ID ('ADM_delete_column', 'P') IS NOT NULL
   DROP procedure ADM_delete_column;
GO

CREATE procedure ADM_delete_column
    @table_name_in  nvarchar(300)
,   @column_name_in nvarchar(300)
AS 
BEGIN
    /*  Author: Matthis ([email protected] at 2019.07.20)
        License CC BY (creativecommons.org)
        Desc:   Administrative procedure that drops columns at MS SQL Server
                - if there is an index or constraint on the column 
                    that will be dropped in advice
                => input parameters are TABLE NAME and COLUMN NAME as STRING
    */
    SET NOCOUNT ON

    --drop index if exist (search first if there is a index on the column)
    declare @idx_name VARCHAR(100)
    SELECT  top 1 @idx_name = i.name
    from    sys.tables t
    join    sys.columns c
    on      t.object_id = c.object_id
    join    sys.index_columns ic
    on      c.object_id = ic.object_id
    and     c.column_id = ic.column_id
    join    sys.indexes i
    on      i.object_id = ic.object_id
    and     i.index_id = ic.index_id
    where   t.name like @table_name_in
    and     c.name like @column_name_in
    if      @idx_name is not null
    begin 
        print concat('DROP INDEX ', @idx_name, ' ON ', @table_name_in)
        exec ('DROP INDEX ' + @idx_name + ' ON ' + @table_name_in)
    end

    --drop fk constraint if exist (search first if there is a constraint on the column)
    declare @fk_name VARCHAR(100)
    SELECT  top 1 @fk_name = CONSTRAINT_NAME 
    from    INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE
    where   TABLE_NAME like @table_name_in
    and     COLUMN_NAME like @column_name_in
    if      @fk_name is not null
    begin 
        print concat('ALTER TABLE ', @table_name_in, ' DROP CONSTRAINT ', @fk_name)
        exec ('ALTER TABLE ' + @table_name_in + ' DROP CONSTRAINT ' + @fk_name)
    end

    --drop column if exist
    declare @column_name VARCHAR(100)
    SELECT  top 1 @column_name = COLUMN_NAME 
    FROM    INFORMATION_SCHEMA.COLUMNS 
    WHERE   COLUMN_NAME like concat('%',@column_name_in,'%')
    if  @column_name is not null
    begin 
        print concat('ALTER TABLE ', @table_name_in, ' DROP COLUMN ', @column_name)
        exec ('ALTER TABLE ' + @table_name_in + ' DROP COLUMN ' + @column_name)
    end
end;
GO


--to run the procedure use this execute and fill the parameters 
execute ADM_delete_column 
    @table_name_in  = ''
,   @column_name_in = ''
    ;
DataMatthis
fonte