Comment déposer une colonne avec contrainte?

138

Comment supprimer une colonne qui a une contrainte par défaut dans SQL Server 2008?

Ma requête est

alter table tbloffers
drop column checkin

Je reçois une erreur en dessous

L'archivage ALTER TABLE DROP COLUMN a échoué car un ou plusieurs objets accèdent à cette colonne.

Quelqu'un peut-il corriger ma requête pour supprimer une colonne avec contrainte?

Robin Michael Poothurai
la source
il peut y avoir des références à cette table à partir d'autres tables qui sont à l'origine de cette erreur.
Pankaj Upadhyay
Pour les nouveaux arrivants qui tombent dessus, consultez ma réponse ci - dessous , si cela fonctionne pour vous, c'est beaucoup plus simple que certaines des autres solutions.
BrainSlugs83
J'ai listé ma question et ma réponse ici
Akash Yellappa

Réponses:

233

Vous devez d'abord supprimer le problème DEFAULT constraint, après cela, vous pouvez supprimer la colonne

alter table tbloffers drop constraint [ConstraintName]
go

alter table tbloffers drop column checkin

Mais l'erreur peut provenir d'autres raisons - par exemple la fonction définie par l'utilisateur ou la vue avec des SCHEMABINDINGoptions définies pour elles.

UPD: Script d'abandon de contraintes entièrement automatisé:

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
la source
1
Merci pour le script automatisé. Fonctionne comme un charme!
kanadianDri3
Merci beaucoup - vous m'avez fait gagner beaucoup de temps. J'ai lié la question que j'ai posée ici
Akash Yellappa
130

Voici une autre façon de supprimer une contrainte par défaut avec un nom inconnu sans avoir à exécuter au préalable une requête distincte pour obtenir le nom de la contrainte:

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
la source
Une superbe réponse, merci. Cependant, j'ai également voté pour la réponse ci-dessus, juste à cause de cette vieille habitude hésitante de sélectionner et d'inspecter avant de décider de l'abandonner.
noogrub
7
Excellente réponse en effet. J'en ai fait une procédure stockée pour plus de commodité / une utilisation future: pastebin.com/2CeXZDh2
Digs
Excellente réponse mais approche toujours manquante quand il y a plus d'une contrainte liée à une colonne. Certains processus stockés similaires à l'article de @Digs avec boucle incluse pourraient être une réponse de 5 étoiles
YeinCM-Qva
27

Vous pouvez également supprimer la colonne et ses contraintes dans une seule instruction plutôt qu'individuellement.

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 

Un SQL dynamique qui recherchera les noms des contraintes de vérification dépendantes et des contraintes par défaut et les déposera avec la colonne est ci-dessous

(mais pas les autres dépendances de colonne possibles telles que les clés étrangères, les contraintes de clé unique et primaire, les colonnes calculées, les index)

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
la source
Cela nécessite cependant que vous connaissiez le nom de la contrainte. S'ils n'ont pas été nommés lors de la création de la table, ils obtiennent un nom généré automatiquement.
Joey
1
@Joey - Il n'y a pas de syntaxe pour supprimer des contraintes sans connaître le nom. C'est un argument requis pour DROP CONSTRAINT voir la grammaire. Si vous ne nommez pas les contraintes explicitement, vous devrez rechercher le nom que SQL Server a généré pour cela, par exemple selon la réponse de marc. Mais après avoir découvert cela, vous pouvez toujours supprimer la contrainte et la colonne en même temps.
Martin Smith
Bon code, j'avais besoin de supprimer plusieurs contraintes à la fois, mais pas la colonne. Votre alter a fait l'affaire. Merci!!
htm11h
26

Trouvez la contrainte par défaut avec cette requête ici:

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

Cela vous donne le nom de la contrainte par défaut, ainsi que le nom de la table et de la colonne.

Lorsque vous disposez de ces informations, vous devez d'abord supprimer la contrainte par défaut:

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

et puis vous pouvez déposer la colonne

ALTER TABLE dbo.YourTable DROP COLUMN YourColumn
marc_s
la source
2
Cela ne doit pas être fait séquentiellement. Vous pouvez faire les deux en même temps.
Martin Smith
1
@MartinSmith: OK, super - merci pour le partage! Je n'étais pas au courant de cette possibilité - vous apprenez quelque chose de nouveau chaque jour! :-)
marc_s
Quelqu'un peut-il donner un exemple pour combiner ces deux déclarations. J'ai besoin de quelque chose comme: ALTER TABLE table DROP CONSTRAINT DF_XY DROP COLUMN XYMalheureusement, la syntaxe de cette déclaration n'est pas correcte
My-Name-Is
1
@ My-Name-Is: si vous consultez la réponse de Martin, vous devez mettre une virgule entre les deux DROPcommandes pour que cela fonctionne
marc_s
3

Ce qui suit a fonctionné pour moi contre un backend SQL Azure (en utilisant SQL Server Management Studio), donc YMMV, mais, si cela fonctionne pour vous, c'est waaaaay plus simple que les autres solutions.

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

J'ai le même:

Échec de ALTER TABLE DROP COLUMN car un ou plusieurs objets accèdent à ce message de colonne .

Ma colonne avait un index qui devait d'abord être supprimé. L'utilisation de sys.indexes a fait l'affaire:

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
la source
0

J'ai un peu mis à jour le script de ma version de serveur SQL

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
la source
0

Ce n'est pas toujours simplement une contrainte par défaut qui empêche de supprimer une colonne et parfois les index peuvent également vous empêcher de supprimer la contrainte. J'ai donc écrit une procédure qui supprime tout index ou contrainte sur une colonne et la colonne elle-même à la fin.

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
la source