Come eliminare la colonna con vincolo?


138

Come eliminare una colonna con vincolo predefinito in SQL Server 2008?

La mia domanda è

alter table tbloffers
drop column checkin

Ricevo sotto errore

Il check-in ALTER TABLE DROP COLUMN non è riuscito perché uno o più oggetti accedono a questa colonna.

Qualcuno può correggere la mia query per eliminare una colonna con vincolo?


potrebbero esserci dei riferimenti a questa tabella da altre tabelle che stanno causando questo errore.
Pankaj Upadhyay,

Per i nuovi arrivati ​​che si imbattono in questo, controlla la mia risposta di seguito , se funziona per te, è molto più semplice di alcune delle altre soluzioni.
BrainSlugs83

Ho elencato la mia domanda e risposta qui
Akash Yellappa,

Risposte:


233

Per prima cosa dovresti eliminare la problematica DEFAULT constraint, dopodiché puoi eliminare la colonna

alter table tbloffers drop constraint [ConstraintName]
go

alter table tbloffers drop column checkin

Ma l'errore può apparire per altri motivi, ad esempio la funzione definita dall'utente o la vista con l' SCHEMABINDINGopzione impostata per loro.

UPD: eliminazione completamente automatica dello script dei vincoli:

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

1
Grazie per lo script automatico. Funziona come un fascino!
kanadianDri3

Grazie mille - mi hai risparmiato un sacco di tempo. Ho collegato la domanda che ho posto qui
Akash Yellappa,

130

Ecco un altro modo per eliminare un vincolo predefinito con un nome sconosciuto senza dover prima eseguire una query separata per ottenere il nome del vincolo:

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)

Una risposta superba, grazie. Ho anche votato a favore della risposta di cui sopra, però, solo per la vecchia e vistosa abitudine di SELEZIONARE e ispezionare prima di decidere di abbandonare.
Noogrub,

7
Ottima risposta davvero. Ho creato una procedura memorizzata per praticità / uso futuro: pastebin.com/2CeXZDh2
Digs

Risposta eccellente ma approccio ancora mancante quando ci sono più vincoli legati a una colonna. Alcuni proc memorizzati simili al post di @Digs con loop incluso potrebbero essere una risposta a 5 stelle
YeinCM-Qva

27

Puoi anche eliminare la colonna e i suoi vincoli in una singola istruzione anziché 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 

Di seguito sono riportati alcuni SQL dinamici che cercheranno i nomi dei vincoli di controllo dipendenti e vincoli predefiniti e li rilasceranno insieme alla colonna

(ma non altre possibili dipendenze delle colonne come chiavi esterne, vincoli di chiavi univoci e primari, colonne calcolate, indici)

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

Ciò richiede tuttavia di conoscere il nome del vincolo. Se non sono stati nominati durante la creazione della tabella, ottengono un nome generato automaticamente.
Joey,

1
@Joey - Non esiste sintassi per eliminare i vincoli senza conoscere il nome. È un argomento obbligatorio per DROP CONSTRAINT visualizzare la grammatica Se non si nominano esplicitamente i vincoli, è necessario cercare il nome SQL Server generato per esso, ad es. Come per la risposta di marc. Ma avendo scoperto che puoi ancora eliminare il vincolo e la colonna allo stesso tempo.
Martin Smith,

Bel codice, avevo bisogno di eliminare i vincoli multipli contemporaneamente, ma non la colonna. Il tuo alter ha fatto il trucco. Grazie!!
h11

26

Trova il vincolo predefinito con questa query qui:

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

Questo ti dà il nome del vincolo predefinito, così come il nome della tabella e della colonna.

Quando si dispone di tali informazioni, è necessario eliminare innanzitutto il vincolo predefinito:

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

e quindi puoi rilasciare la colonna

ALTER TABLE dbo.YourTable DROP COLUMN YourColumn

2
Non deve essere fatto in sequenza. Puoi farli entrambi contemporaneamente.
Martin Smith,

1
@MartinSmith: OK, fantastico - grazie per averlo condiviso! Non ero a conoscenza di questa possibilità: impari qualcosa di nuovo ogni giorno! :-)
marc_s,

Qualcuno può fornire un esempio su come combinare queste due affermazioni. Ho bisogno di qualcosa del tipo: ALTER TABLE table DROP CONSTRAINT DF_XY DROP COLUMN XYpurtroppo la sintassi di questa affermazione non è corretta
My-Name-Is

1
@ My-Name-Is: se controlli la risposta di Martin, devi mettere una virgola tra i due DROPcomandi per farlo funzionare
marc_s,

3

Quanto segue ha funzionato per me contro un back-end di SQL Azure (usando SQL Server Management Studio), quindi YMMV, ma, se funziona per te, è sicuramente più semplice delle altre soluzioni.

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

1

Ho avuto lo stesso:

ALTER TABLE DROP COLUMN non è riuscito perché uno o più oggetti accedono a questo messaggio di colonna .

La mia colonna aveva un indice che doveva essere prima eliminato. L'uso di sys.indexes ha fatto il trucco:

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

0

Ho aggiornato un po 'lo script con la versione del mio server 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;

0

Non è sempre solo un vincolo predefinito che impedisce di eliminare una colonna e talvolta gli indici possono anche impedirti di eliminare il vincolo. Quindi ho scritto una procedura che elimina qualsiasi indice o vincolo su una colonna e la colonna stessa alla fine.

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 (matthis@online.ms 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 = ''
    ;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.