2008-11-24 13 views
59

Ich habe eine Spalte mit einer "DEFAULT" Einschränkung. Ich möchte ein Skript erstellen, das diese Spalte löscht.SQL Server 2005-Drop-Spalte mit Einschränkungen

Das Problem ist, dass es diesen Fehler zurückgibt:

Msg 5074, Level 16, State 1, Line 1 
The object 'DF__PeriodSce__IsClo__4BCC3ABA' is dependent on column 'IsClosed'. 
Msg 4922, Level 16, State 9, Line 1 
ALTER TABLE DROP COLUMN IsClosed failed because one or more objects access this column. 

ich nicht einen einfachen Weg finden könnte eine Spalte und alle damit verbundenen Einschränkungen (nur große Skripte zu löschen, schauen Sie in die Systemtabelle gefunden .. Es muss (!!) ein "netter" Weg sein.

Und da der Name der DEFAULT-Einschränkung zufällig generiert wurde, kann ich ihn nicht mit dem Namen löschen.


aktualisieren:
Der Constraint-Typ "STANDARD".

Ich sah die Lösungen, die Sie alle vorgeschlagen, aber ich finde sie alle wirklich "schmutzig" ... Denkst du nicht? Ich weiß nicht, ob es mit Oracle oder MySQL ist, aber es ist möglich, so etwas wie zu tun:

DROP COLUMN xxx CASCADE CONSTRAINTS 

Und es fällt alle damit verbundenen Einschränkungen ... Oder zumindest sinkt automatisch die zu dieser Spalte zugeordnet Bedingungen (mindestens CHECK Constraints!)

Gibt es in MSSQL nichts dergleichen?

Antwort

19

Diese Abfrage findet Standardbedingungen für eine bestimmte Tabelle. Es aint ziemlich, ich bin einverstanden:

select 
    col.name, 
    col.column_id, 
    col.default_object_id, 
    OBJECTPROPERTY(col.default_object_id, N'IsDefaultCnst') as is_defcnst, 
    dobj.name as def_name 
from sys.columns col 
    left outer join sys.objects dobj 
     on dobj.object_id = col.default_object_id and dobj.type = 'D' 
where col.object_id = object_id(N'dbo.test') 
and dobj.name is not null 

[EDIT] Aktualisiert pro Julien N Kommentar

+1

Ihre Antwort ist richtig, aber sie tut nicht das Gleiche wie meins. Sie erhalten die Einschränkungen von einer bestimmten Tabelle, meine bekommt alle Einschränkungen. Zwei Dinge: Syntaxfehler (entfernen Sie vorher das Komma) und Sie sollten "und dobj.name ist nicht null" in der WHERE-Klausel hinzufügen, um die nicht relevanten Ergebnisse zu entfernen. –

+1

+1, brauchte nur das. –

0

Was meinen Sie zufällig generiert? Sie können die Einschränkungen für die spezifische Spalte in Management Studio oder in der sys.tables-Ansicht nachschlagen und herausfinden, um welche Namen es sich handelt.

Anschließend können Sie Ihr Skript so ändern, dass die Abhängigkeiten gelöscht werden, bevor die Spalte gelöscht wird. Welche Art von Einschränkung ist das? Wenn es sich um eine Fremdschlüsseleinschränkung handelt, stellen Sie sicher, dass dies die Datenintegrität in Ihrer Datenbank nicht beeinträchtigt.

3

Sie können die Constraint-Namen abrufen, indem Sie die Systemansichten des information_schema abfragen.

select CONSTRAINT_NAME from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_NAME = '<tablename>' AND COLUMN_NAME = 'IsClosed' 
+2

Dies gibt keine Standardeinschränkungen zurück, wie unten erwähnt – edosoft

1

Ich glaube, dass das explizite Löschen der Einschränkungen vor dem Löschen der Spalte eine "sauberere" Lösung ist. Auf diese Weise werden Einschränkungen, die Ihnen möglicherweise nicht bewusst sind, nicht gelöscht. Wenn der Abbruch weiterhin fehlschlägt, wissen Sie, dass weitere Einschränkungen bestehen bleiben. Ich mag es, genau zu kontrollieren, was mit meiner Datenbank passiert.

Plus, Scripting der Tropfen garantiert explizit das Skript und hoffentlich die Ergebnisse in genau der Art, wie Sie beabsichtigen, wiederholbar.

+1

"Sie fallen keine Einschränkungen aus, die Ihnen vielleicht nicht bewusst sind" - stimme ich zu. Aber nicht für Einschränkungen, die nur für die Spalte fallen gelassen ... Ich denke, ich muss die Datenbank anfordern, um den Namen der Einschränkung zu kennen, um in der Lage sein, es fallen zu lassen ... –

5
> select CONSTRAINT_NAME from INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE 
> WHERE TABLE_NAME = '<tablename>' AND COLUMN_NAME = 'IsClosed' 

Es ist nicht die richtige Lösung, da es wird hier erklärt: http://msdn.microsoft.com/en-us/library/aa175912.aspx dass:

Unfortunately, the name of the column default constraint isn't kept in the ANSI COLUMNS view, so you must go back to the system tables to find the name

Der einzige Weg, ich den Namen der DEFAULT-Einschränkung bekommen gefunden ist diese Anfrage:

select 
    t_obj.name    as TABLE_NAME 
    ,c_obj.name    as CONSTRAINT_NAME 
    ,col.name    as COLUMN_NAME 

from sysobjects c_obj 
join sysobjects t_obj on c_obj.parent_obj = t_obj.id 
join sysconstraints con on c_obj.id = con.constid 
join syscolumns col on t_obj.id = col.id 
      and con.colid = col.colid 
where 
    c_obj.xtype = 'D' 

Bin ich der Einzige, der es findet verrückt, um nicht in der Lage zu sein, eine Einschränkung zu löschen, die nur die Spalten betrifft, die ich versuche zu fallen?
Ich muss eine Anfrage mit 3 Joins ausführen, nur um den Namen zu erhalten ...

+0

Siehe meine Antwort für ein wenig cleaner query – edosoft

+0

Nein, du bist nicht verrückt! – briantyler

+1

Wie String-Splitting/Verkettung ist dies eine weitere Situation, in der jeder seine eigene kludgy-Problemumgehung implementieren muss, wenn die Funktionalität mit SQL Server selbst geliefert werden sollte. – sayap

-1

Generieren Sie Skripte für die Tabelle. Dort finden Sie den Namen aller Einschränkungen.

62

Hier ist ein Skript, das die Spalte zusammen mit seiner Standardeinschränkung gelöscht. Ersetzen Sie MYTABLENAME und MYCOLUMNNAME entsprechend.

declare @constraint_name sysname, @sql nvarchar(max) 

select @constraint_name = name 
from sys.default_constraints 
where parent_object_id = object_id('MYTABLENAME') 
AND type = 'D' 
AND parent_column_id = (
    select column_id 
    from sys.columns 
    where object_id = object_id('MYTABLENAME') 
    and name = 'MYCOLUMNNAME' 
    ) 

set @sql = N'alter table MYTABLENAME drop constraint ' + @constraint_name 
exec sp_executesql @sql 

alter table MYTABLENAME drop column MYCOLUMNNAME 

go 
+0

schön, das sieht aus wie ich danach bin. – CRice

+0

Ich musste tun 'Alter Tabelle MYTABLENAME Drop-Spalte MYCOLUMNNAME' –

+0

@Bill Vielen Dank! Ich habe es behoben. –

14

Vielleicht könnte es ein wenig mehr helfen:

declare @tablename nvarchar(200) 
declare @colname nvarchar(200) 
declare @default sysname, @sql nvarchar(max) 

set @tablename = 'your table' 
set @colname = 'column to drop' 

select @default = name 
from sys.default_constraints 
where parent_object_id = object_id(@tablename) 
AND type = 'D' 
AND parent_column_id = (
    select column_id 
    from sys.columns 
    where object_id = object_id(@tablename) 
    and name = @colname 
    ) 

set @sql = N'alter table ' + @tablename + ' drop constraint ' + @default 
exec sp_executesql @sql 

set @sql = N'alter table ' + @tablename + ' drop column ' + @colname 
exec sp_executesql @sql 

Nur müssen die @tablename & @colname Variablen setzen, die Spalte zu löschen.

+0

wirklich gute Antwort, zusammen mit [@ Jeremy Steins Antwort] (http://stackoverflow.com/a/1171895/662581) hilf mir mein eigenes zu erstellen, um mein Problem zu lösen. –

5

Ich denke auch, es ist ein Manko in SQL-Server, keine Kaskadierung Drop zur Verfügung zu haben. Ich arbeitete mich um sie durch die Systemtabellen in der gleichen Art und Weise abfragt, wie andere Leute hier beschrieben:

  • INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE nur Fremdschlüssel, Primärschlüssel und eindeutige Einschränkungen auflistet.
  • Die einzige Möglichkeit, nach Standardeinschränkungen zu suchen, besteht darin, sie in sys.default_constraints zu suchen.
  • Was hier noch nicht erwähnt wurde, ist, dass Indizes auch das Löschen einer Spalte fehlschlagen, so müssen Sie auch alle Indizes, die Ihre Spalte verwenden, bevor Sie mit dem Löschen einer Spalte fortfahren können.

Das resultierende Skript ist nicht schön, aber ich habe es in einer gespeicherten Prozedur, um es wieder zu verwenden:

CREATE PROCEDURE DropColumnCascading @tablename nvarchar(500), @columnname nvarchar(500) 
AS 

SELECT CONSTRAINT_NAME, 'C' AS type 
INTO #dependencies 
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_NAME = @tablename AND COLUMN_NAME = @columnname 

INSERT INTO #dependencies 
select d.name, 'C' 
from sys.default_constraints d 
join sys.columns c ON c.column_id = d.parent_column_id AND c.object_id = d.parent_object_id 
join sys.objects o ON o.object_id = d.parent_object_id 
WHERE o.name = @tablename AND c.name = @columnname 

INSERT INTO #dependencies 
SELECT i.name, 'I' 
FROM sys.indexes i 
JOIN sys.index_columns ic ON ic.index_id = i.index_id and ic.object_id=i.object_id 
JOIN sys.columns c ON c.column_id = ic.column_id and c.object_id=i.object_id 
JOIN sys.objects o ON o.object_id = i.object_id 
where o.name = @tableName AND i.type=2 AND c.name = @columnname AND is_unique_constraint = 0 

DECLARE @dep_name nvarchar(500) 
DECLARE @type nchar(1) 

DECLARE dep_cursor CURSOR 
FOR SELECT * FROM #dependencies 

OPEN dep_cursor 

FETCH NEXT FROM dep_cursor 
INTO @dep_name, @type; 

DECLARE @sql nvarchar(max) 

WHILE @@FETCH_STATUS = 0 
BEGIN 
    SET @sql = 
     CASE @type 
      WHEN 'C' THEN 'ALTER TABLE [' + @tablename + '] DROP CONSTRAINT [' + @dep_name + ']' 
      WHEN 'I' THEN 'DROP INDEX [' + @dep_name + '] ON dbo.[' + @tablename + ']' 
     END 
    print @sql 
    EXEC sp_executesql @sql 
    FETCH NEXT FROM dep_cursor 
    INTO @dep_name, @type; 
END 

DEALLOCATE dep_cursor 

DROP TABLE #dependencies 

SET @sql = 'ALTER TABLE [' + @tablename + '] DROP COLUMN [' + @columnname + ']' 

print @sql 
EXEC sp_executesql @sql 
0

Ich lief in diesen. Sie können die Spalte mit Einschränkungen mithilfe der MSSQL-Entwurfsansicht löschen. Klicken Sie mit der rechten Maustaste auf die Spalte, die Sie löschen möchten (mit oder ohne Einschränkungen), und Sie können diese ohne Probleme löschen. Ha .. Ich sah schon blöd aus.

+1

Ja, Sie können es über den Enterprise Manager löschen. Das Ziel war, es per Skript zu machen. Ich hatte keinen direkten Zugriff auf die SQL Server-Instanz. –

1

Es ist nicht immer eine Option, den Namen der Contraint zu suchen oder MSSQL Design View zu verwenden. Ich möchte derzeit ein Skript zum Löschen einer Spalte mit einem Contraint erstellen. Die Verwendung des Namens ist keine Option, da der Name generiert wird und ich das Skript in verschiedenen Umgebungen Dev/Sys/Prod/etc verwenden möchte. Die Verwendung des Namens ist dann nicht möglich, da sich die Contraint-Namen pro Umgebung unterscheiden. Ich werde wahrscheinlich in die Systemtabelle schauen müssen, aber ich stimme zu, dass es eine einfachere Option geben sollte.

3

Die Antwort von pvolders war genau das, was ich brauchte, aber es verpasste Statistiken, die Fehler verursachten. Dies ist derselbe Code, abzüglich der Erstellung einer gespeicherten Prozedur, sowie das Aufzählen von Statistiken und deren Löschen. Dies ist das Beste, was ich mir vorstellen kann. Wenn es einen besseren Weg gibt zu bestimmen, welche Statistiken gelöscht werden müssen, fügen Sie bitte hinzu.

DECLARE @tablename nvarchar(500), 
     @columnname nvarchar(500) 

SELECT @tablename = 'tblProject', 
     @columnname = 'CountyKey' 


SELECT CONSTRAINT_NAME, 'C' AS type 
INTO #dependencies 
FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE WHERE TABLE_NAME = @tablename AND COLUMN_NAME = @columnname 

INSERT INTO #dependencies 
select d.name, 'C' 
from sys.default_constraints d 
join sys.columns c ON c.column_id = d.parent_column_id AND c.object_id = d.parent_object_id 
join sys.objects o ON o.object_id = d.parent_object_id 
WHERE o.name = @tablename AND c.name = @columnname 

INSERT INTO #dependencies 
SELECT i.name, 'I' 
FROM sys.indexes i 
JOIN sys.index_columns ic ON ic.index_id = i.index_id and ic.object_id=i.object_id 
JOIN sys.columns c ON c.column_id = ic.column_id and c.object_id=i.object_id 
JOIN sys.objects o ON o.object_id = i.object_id 
where o.name = @tableName AND i.type=2 AND c.name = @columnname AND is_unique_constraint = 0 

INSERT INTO #dependencies 
SELECT s.NAME, 'S' 
FROM sys.stats AS s 
INNER JOIN sys.stats_columns AS sc 
    ON s.object_id = sc.object_id AND s.stats_id = sc.stats_id 
INNER JOIN sys.columns AS c 
    ON sc.object_id = c.object_id AND c.column_id = sc.column_id 
WHERE s.object_id = OBJECT_ID(@tableName) 
AND c.NAME = @columnname 
AND s.NAME LIKE '_dta_stat%' 

DECLARE @dep_name nvarchar(500) 
DECLARE @type nchar(1) 

DECLARE dep_cursor CURSOR 
FOR SELECT * FROM #dependencies 

OPEN dep_cursor 

FETCH NEXT FROM dep_cursor 
INTO @dep_name, @type; 

DECLARE @sql nvarchar(max) 

WHILE @@FETCH_STATUS = 0 
BEGIN 
    SET @sql = 
     CASE @type 
      WHEN 'C' THEN 'ALTER TABLE [' + @tablename + '] DROP CONSTRAINT [' + @dep_name + ']' 
      WHEN 'I' THEN 'DROP INDEX [' + @dep_name + '] ON dbo.[' + @tablename + ']' 
      WHEN 'S' THEN 'DROP STATISTICS [' + @tablename + '].[' + @dep_name + ']' 
     END 
    print @sql 
    EXEC sp_executesql @sql 
    FETCH NEXT FROM dep_cursor 
    INTO @dep_name, @type; 
END 

DEALLOCATE dep_cursor 

DROP TABLE #dependencies 

SET @sql = 'ALTER TABLE [' + @tablename + '] DROP COLUMN [' + @columnname + ']' 

print @sql 
EXEC sp_executesql @sql 
3

einfach auf Jeremy Stein Antwort zu bauen, habe ich eine gespeicherte Prozedur für diesen, und es so einrichten, es verwendet werden kann, um eine Spalte zu löschen, das hat oder über keine Standardeinschränkungen. Es ist nicht wirklich effizient, da sys.columns zweimal abgefragt wird, aber es funktioniert.

+0

Der Stealth-Ansatz gewinnt! – Bern

Verwandte Themen