Wie kann ich eine Spalte mit Einschränkung löschen?

138

Wie lösche ich eine Spalte mit der Standardeinschränkung in SQL Server 2008?

Meine Anfrage ist

alter table tbloffers
drop column checkin

Ich erhalte unter Fehler

Das Einchecken der ALTER TABLE DROP COLUMN ist fehlgeschlagen, da ein oder mehrere Objekte auf diese Spalte zugreifen.

Kann jemand meine Abfrage korrigieren, um eine Spalte mit Einschränkungen zu löschen?

Robin Michael Poothurai
quelle
Möglicherweise gibt es Verweise auf diese Tabelle aus anderen Tabellen, die diesen Fehler verursachen.
Pankaj Upadhyay
Für Neuankömmlinge, die darauf stoßen , lesen Sie meine Antwort unten . Wenn es für Sie funktioniert, ist es viel einfacher als einige der anderen Lösungen.
BrainSlugs83
Ich habe meine Frage und Antwort hier
Akash Yellappa

Antworten:

233

Zuerst sollten Sie das Problem löschen DEFAULT constraint, danach können Sie die Spalte löschen

alter table tbloffers drop constraint [ConstraintName]
go

alter table tbloffers drop column checkin

Der Fehler kann jedoch auch aus anderen Gründen auftreten - beispielsweise aufgrund der benutzerdefinierten Funktion oder Ansicht SCHEMABINDING, für die eine Option festgelegt wurde.

UPD: Vollständig automatisiertes Löschen des Einschränkungsskripts:

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
quelle
1
Vielen Dank für das automatisierte Skript. Klappt wunderbar!
KanadianDri3
Vielen Dank - Sie haben mir viel Zeit gespart. Ich habe die Frage, die ich gestellt habe, hier
verlinkt
130

Hier ist eine andere Möglichkeit, eine Standardeinschränkung mit einem unbekannten Namen zu löschen, ohne zuerst eine separate Abfrage ausführen zu müssen, um den Einschränkungsnamen abzurufen:

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
quelle
Eine hervorragende Antwort, danke. Ich habe die obige Antwort jedoch auch positiv bewertet, nur aus der alten Gewohnheit heraus, zuerst zu wählen und zu inspizieren, bevor ich mich entschloss, sie fallen zu lassen.
Noogrub
7
Tolle Antwort. Ich habe eine gespeicherte Prozedur daraus für die Bequemlichkeit / zukünftige Verwendung gemacht: pastebin.com/2CeXZDh2
Digs
Hervorragende Antwort, aber immer noch fehlender Ansatz, wenn mehr als eine Einschränkung an eine Spalte gebunden ist. Einige gespeicherte Prozesse, die dem Beitrag von @Digs mit Schleife ähnlich sind, könnten eine Antwort mit 5 Sternen sein
YeinCM-Qva
27

Sie können die Spalte und ihre Einschränkung (en) auch in einer einzelnen Anweisung und nicht einzeln ablegen.

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 

Nachfolgend finden Sie einige dynamische SQL-Anweisungen, mit denen die Namen abhängiger Überprüfungsbeschränkungen und Standardeinschränkungen nachgeschlagen und zusammen mit der Spalte gelöscht werden

(aber keine anderen möglichen Spaltenabhängigkeiten wie Fremdschlüssel, eindeutige und Primärschlüsseleinschränkungen, berechnete Spalten, Indizes)

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
quelle
Dies erfordert jedoch, dass Sie den Namen der Einschränkung kennen. Wenn sie bei der Tabellenerstellung nicht benannt wurden, erhalten sie einen automatisch generierten Namen.
Joey
1
@Joey - Es gibt keine Syntax zum Löschen von Einschränkungen, ohne den Namen zu kennen. Es ist ein erforderliches Argument, DROP CONSTRAINT um die Grammatik zu sehen. Wenn Sie die Einschränkungen nicht explizit benennen, müssen Sie den Namen nachschlagen, den SQL Server dafür generiert hat, z. B. gemäß der Antwort von Marc. Nachdem Sie dies herausgefunden haben, können Sie die Einschränkung und die Spalte gleichzeitig löschen.
Martin Smith
Netter Code, ich musste mehrere Einschränkungen gleichzeitig löschen, aber nicht die Spalte. Dein Alter hat es geschafft. Vielen Dank!!
htm11h
26

Hier finden Sie die Standardeinschränkung für diese Abfrage:

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

Hier erhalten Sie den Namen der Standardeinschränkung sowie den Tabellen- und Spaltennamen.

Wenn Sie diese Informationen haben, müssen Sie zuerst die Standardeinschränkung löschen:

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

und dann können Sie die Spalte löschen

ALTER TABLE dbo.YourTable DROP COLUMN YourColumn
marc_s
quelle
2
Es muss nicht nacheinander erfolgen. Sie können beide gleichzeitig ausführen.
Martin Smith
1
@ MartinSmith: OK, großartig - danke fürs Teilen! Diese Möglichkeit war mir nicht bewusst - Sie lernen jeden Tag etwas Neues! :-)
marc_s
Kann jemand bitte ein Beispiel geben, wie diese beiden Aussagen kombiniert werden können? Ich brauche etwas wie: ALTER TABLE table DROP CONSTRAINT DF_XY DROP COLUMN XYLeider ist die Syntax dieser Anweisung nicht korrekt
My-Name-Is
1
@ My-Name-Is: Wenn Sie Martins Antwort überprüfen, müssen Sie ein Komma zwischen die beiden DROPBefehle setzen, damit dies funktioniert
marc_s
3

Das Folgende funktionierte für mich gegen ein SQL Azure-Backend (mit SQL Server Management Studio), also YMMV, aber wenn es für Sie funktioniert, ist es einfacher als die anderen Lösungen.

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

Ich habe das gleiche:

ALTER TABLE DROP COLUMN ist fehlgeschlagen, weil ein oder mehrere Objekte auf diese Spaltennachricht zugreifen .

Meine Spalte hatte einen Index, der zuerst gelöscht werden musste. Die Verwendung von sys.indexes hat den Trick gemacht:

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
quelle
0

Ich habe das Skript ein wenig auf meine SQL Server-Version aktualisiert

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
quelle
0

Es ist nicht immer nur eine Standardeinschränkung, die verhindert, dass eine Spalte gelöscht wird, und manchmal können Indizes Sie auch daran hindern, die Einschränkung zu löschen. Also habe ich eine Prozedur geschrieben, die jeden Index oder jede Einschränkung für eine Spalte und die Spalte selbst am Ende löscht.

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
quelle