Jak upuścić kolumnę z ograniczeniem?

138

Jak usunąć kolumnę, która ma ograniczenie domyślne w programie SQL Server 2008?

Moje pytanie brzmi

alter table tbloffers
drop column checkin

Otrzymuję poniżej błąd

Zameldowanie ALTER TABLE DROP COLUMN nie powiodło się, ponieważ co najmniej jeden obiekt uzyskuje dostęp do tej kolumny.

Czy ktoś może poprawić moje zapytanie, aby usunąć kolumnę z ograniczeniem?

Robin Michael Poothurai
źródło
mogą występować odwołania do tej tabeli z innych tabel, które powodują ten błąd.
Pankaj Upadhyay
Dla nowych przybyszów, którzy natkną się na to, sprawdź moją odpowiedź poniżej , jeśli to działa dla Ciebie, jest o wiele prostsze niż niektóre inne rozwiązania.
BrainSlugs83
Tutaj
Akash Yellappa

Odpowiedzi:

233

Najpierw upuść problematyczną DEFAULT constraint, po czym możesz upuścić kolumnę

alter table tbloffers drop constraint [ConstraintName]
go

alter table tbloffers drop column checkin

Ale błąd może wynikać z innych powodów - na przykład funkcji zdefiniowanej przez użytkownika lub widoku z SCHEMABINDINGustawioną dla nich opcją.

UPD: Całkowicie zautomatyzowany skrypt usuwania ograniczeń:

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
źródło
1
Dzięki za automatyczny skrypt. Działa jak marzenie!
kanadianDri3
Dziękuję bardzo - zaoszczędziliście mi dużo czasu.
Podłączyłem
130

Oto inny sposób na usunięcie domyślnego ograniczenia o nieznanej nazwie bez konieczności uruchamiania oddzielnego zapytania w celu uzyskania nazwy ograniczenia:

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
źródło
Wspaniała odpowiedź, dziękuję. Głosowałem również za powyższą odpowiedzią, po prostu z powodu tego wzdrygającego się starego nawyku WYBIERANIA i sprawdzania najpierw przed podjęciem decyzji o rezygnacji.
noogrub
7
Naprawdę świetna odpowiedź. Zrobiłem z tego procedurę składowaną dla wygody / przyszłego użytku: pastebin.com/2CeXZDh2
Digs
Doskonała odpowiedź, ale wciąż brakuje podejścia, gdy istnieje więcej niż jedno ograniczenie związane z kolumną. Niektóre przechowywane procesy podobne do posta @Digs z dołączoną pętlą mogą zawierać 5 gwiazdek
YeinCM-Qva
27

Możesz również upuścić kolumnę i jej ograniczenia w pojedynczej instrukcji, a nie pojedynczo.

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 

Poniżej znajduje się kilka dynamicznych instrukcji SQL, które wyszukają nazwy zależnych ograniczeń sprawdzających i domyślnych ograniczeń i usuwają je wraz z kolumną

(ale nie inne możliwe zależności kolumn, takie jak klucze obce, ograniczenia unikalności i klucza podstawowego, kolumny obliczane, indeksy)

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
źródło
Wymaga to jednak znajomości nazwy ograniczenia. Jeśli nawet nie zostały nazwane podczas tworzenia tabeli, otrzymają automatycznie wygenerowaną nazwę.
Joey
1
@Joey - nie ma składni pozwalającej usuwać ograniczenia bez znajomości nazwy. Jest to argument wymagany, aby DROP CONSTRAINT zobaczyć gramatykę. Jeśli nie nazwiesz ograniczeń jawnie, będziesz musiał sprawdzić nazwę wygenerowaną przez SQL Server, np. Zgodnie z odpowiedzią marca. Ale gdy się o tym dowiesz, nadal możesz jednocześnie usunąć ograniczenie i kolumnę.
Martin Smith
Niezły kod, musiałem od razu porzucić wiele ograniczeń, ale nie kolumnę. Twój alter załatwił sprawę. Dzięki!!
htm11h
26

Znajdź domyślne ograniczenie z tym zapytaniem tutaj:

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

W ten sposób otrzymasz nazwę domyślnego ograniczenia, a także nazwę tabeli i kolumny.

Gdy masz te informacje, musisz najpierw usunąć domyślne ograniczenie:

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

a następnie możesz upuścić kolumnę

ALTER TABLE dbo.YourTable DROP COLUMN YourColumn
marc_s
źródło
2
Nie trzeba tego robić po kolei. Możesz to zrobić jednocześnie.
Martin Smith
1
@MartinSmith: OK, świetnie - dzięki za udostępnienie! Nie byłam świadoma takiej możliwości - każdego dnia uczysz się czegoś nowego! :-)
marc_s
Czy ktoś może podać przykład połączenia tych dwóch stwierdzeń. Potrzebuję czegoś takiego: ALTER TABLE table DROP CONSTRAINT DF_XY DROP COLUMN XYNiestety składnia tego stwierdzenia jest nieprawidłowa
My-Name-Is
1
@ My-Name-Is: jeśli sprawdzisz odpowiedź Martina, musisz wstawić przecinek między dwoma DROPpoleceniami, aby to zadziałało
marc_s
3

Poniższe rozwiązania działały dla mnie w odniesieniu do zaplecza SQL Azure (przy użyciu SQL Server Management Studio), więc YMMV, ale jeśli działa dla Ciebie, jest to znacznie prostsze niż inne rozwiązania.

ALTER TABLE MyTable
    DROP CONSTRAINT FK_MyColumn
    CONSTRAINT DK_MyColumn
    -- etc...
    COLUMN MyColumn
GO
BrainSlugs83
źródło
1

Mam to samo:

ALTER TABLE DROP COLUMN nie powiodło się, ponieważ co najmniej jeden obiekt uzyskuje dostęp do tego komunikatu kolumny .

Moja kolumna miała indeks, który należało najpierw usunąć. Użycie sys.indexes załatwiło sprawę :

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
źródło
0

Zaktualizowałem trochę skrypt do mojej wersji serwera 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
źródło
0

Nie zawsze jest to tylko domyślne ograniczenie, które zapobiega upuszczeniu kolumny, a czasami indeksy mogą również blokować usunięcie ograniczenia. Napisałem więc procedurę, która porzuca indeks lub ograniczenie w kolumnie, a na końcu samą kolumnę.

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
źródło