2012-10-04 16 views
13

Chcę napisać polecenie sql, aby usunąć wszystkie więzy we wszystkich tabelach. Szukałem w Internecie i znalazłem następujące, które działa dobrze, jeśli baza danych jest mała i nie jest skomplikowana.Jak usunąć wszystkie ograniczenia klucza obcego we wszystkich tabelach?

DECLARE @name VARCHAR(128) 
DECLARE @constraint VARCHAR(254) 
DECLARE @SQL VARCHAR(254) 
DECLARE @schema VARCHAR(128) 

SELECT @name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' ORDER BY TABLE_NAME) 
SELECT @schema = (SELECT TOP 1 schema_name(schema_id) FROM sys.objects WHERE [name] = @name) 

WHILE @name is not null 
BEGIN 
    SELECT @constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' AND TABLE_NAME = @name ORDER BY CONSTRAINT_NAME) 
    WHILE @constraint IS NOT NULL 
    BEGIN 
     SELECT @SQL = 'ALTER TABLE ' + @schema + '.[' + RTRIM(@name) +'] DROP CONSTRAINT [' + RTRIM(@constraint) +']' 
     EXEC (@SQL) 
     PRINT 'Dropped FK Constraint: ' + @constraint + ' on ' + @name 
     SELECT @constraint = (SELECT TOP 1 CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' AND CONSTRAINT_NAME <> @constraint AND TABLE_NAME = @name ORDER BY CONSTRAINT_NAME) 
    END 
SELECT @name = (SELECT TOP 1 TABLE_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE constraint_catalog=DB_NAME() AND CONSTRAINT_TYPE = 'FOREIGN KEY' ORDER BY TABLE_NAME) 
SELECT @schema = (SELECT TOP 1 schema_name(schema_id) FROM sys.objects WHERE [name] = @name) 
END 
GO 

Nie działa, jeśli uruchomię go z bardziej złożoną bazą danych lub nawet z AdventureWork. Pokazuje niektóre eros, jak poniżej.

Msg 3728, Level 16, State 1, Line 1 
'FK_ap_invoice_modification_type_id' is not a constraint. 
Msg 3727, Level 16, State 0, Line 1 
Could not drop constraint. See previous errors. 
Msg 3725, Level 16, State 0, Line 1 
The constraint 'PK_ap_invoice' is being referenced by table '_drop_now_ap_invoice_detail', foreign key constraint 'FK_ap_invoice_detail_ap_invoice'. 
Msg 3727, Level 16, State 0, Line 1 
Could not drop constraint. See previous errors. 

Powodem jest fakt, że niektóre odwołania do FK są tworzone przez inną tabelę. Muszę uruchomić ten skrypt kilka razy, dopóki baza danych nie będzie czysta.

Chcę wiedzieć, w jaki sposób wyczyścić wszystkie FK w bazie danych.

+1

Według tytule chcesz usunąć klucze obce. Ale twoje ostatnie pytanie wymaga "oczyszczenia wszystkiego", w tym tabel, procedur składowanych, funkcji. Co masz na myśli mówiąc o tym? – Yaroslav

+0

@Yaroslav Dziękuję za uwagę. Jestem pełnowymiarowy z zadaniami. Aktualizuję tylko szczegóły zapytania, aby dopasować je do tytułu. – Anonymous

+0

Mimo to jestem pewien, że musiałeś sprawdzić na podobne pytania, ale chciałbym poprosić o ponowne przejrzenie rozwiązania tutaj: http://stackoverflow.com/a/1438933/1268844 – Anshu

Odpowiedz

23

Istnieje wiele informacji na ten temat. Sprawdź this detailed answer przez @AaronBertrand. Mówi o tymczasowym wyłączeniu obcych kluczy, ale czytając je wszystkie i modyfikując, będziesz miał niezły scenariusz do gry i wiele osiągniesz.

Z mojej strony mogę zaproponować 2 różne skrypty, aby uzyskać wszystkie klucze obce. W obu przypadkach odkomentuj kod --EXEC (@SQL), aby wykonać swój kod ALTER. Lub możesz poczekać, aż wydrukuje wszystkie klauzule alter, a następnie skopiować wklej, aby je wykonać.

Pierwszy z nich wykorzystuje INFORMATION_SCHEMA dostać ograniczeń:

DECLARE @SQL VARCHAR(MAX)='' 
SELECT @SQL = @SQL + 'ALTER TABLE ' + QUOTENAME(FK.TABLE_SCHEMA) + '.' + QUOTENAME(FK.TABLE_NAME) + ' DROP CONSTRAINT [' + RTRIM(C.CONSTRAINT_NAME) +'];' + CHAR(13) 
--SELECT K_Table = FK.TABLE_NAME, FK_Column = CU.COLUMN_NAME, PK_Table = PK.TABLE_NAME, PK_Column = PT.COLUMN_NAME, Constraint_Name = C.CONSTRAINT_NAME 
    FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C 
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK 
    ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME 
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK 
    ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME 
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU 
    ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME 
INNER JOIN (
      SELECT i1.TABLE_NAME, i2.COLUMN_NAME 
       FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1 
      INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 
       ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME 
      WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY' 
      ) PT 
    ON PT.TABLE_NAME = PK.TABLE_NAME 

--EXEC (@SQL) 

PRINT @SQL 

ten jeden przy użyciu różnych widoków systemowych i stół CTE.

DECLARE @SQL varchar(4000)='' 
;WITH ReferencingFK AS 
(
    SELECT fk.Name AS 'FKName', OBJECT_NAME(fk.parent_object_id) 'ParentTable', 
      cpa.name 'ParentColumnName', OBJECT_NAME(fk.referenced_object_id) 'ReferencedTable', 
      cref.name 'ReferencedColumnName' 
    FROM sys.foreign_keys fk 
    INNER JOIN sys.foreign_key_columns fkc ON fkc.constraint_object_id = fk.object_id 
    INNER JOIN sys.columns cpa ON fkc.parent_object_id = cpa.object_id AND fkc.parent_column_id = cpa.column_id 
    INNER JOIN sys.columns cref ON fkc.referenced_object_id = cref.object_id AND fkc.referenced_column_id = cref.column_id 
) 
SELECT @SQL = @SQL + 'ALTER TABLE ' + ParentTable + ' DROP CONSTRAINT [' + RTRIM(FKName) +'];' + CHAR(13) 
--SELECT FKName, ParentTable, ParentColumnName, ReferencedTable, ReferencedColumnName 
    FROM ReferencingFK 
WHERE ReferencedTable = 'Employee' 
ORDER BY ParentTable, ReferencedTable, FKName 

--EXEC (@SQL) 

PRINT @SQL 
+1

Powinieneś użyć 'varchar (max)' i ''ALTER TABLE' + QUOTENAME (FK.TABLE_SCHEMA) + '.' + QUOTENAME (FK.TABLE_NAME) + "', aby uniknąć błędów składni. –

+0

Dodano @AntoineAubry, thx – Yaroslav

5

najbardziej prosty wariant:

DECLARE @sql nvarchar(MAX) 
SET @sql = '' 

SELECT @sql = @sql + 'ALTER TABLE ' + QUOTENAME(RC.CONSTRAINT_SCHEMA) 
    + '.' + QUOTENAME(KCU1.TABLE_NAME) 
    + ' DROP CONSTRAINT ' + QUOTENAME(rc.CONSTRAINT_NAME) + '; ' 
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS RC 

INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU1 
    ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG 
    AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA 
    AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME 


-- PRINT @sql 
EXECUTE(@sql) 
6

I poprawiły pierwszy skrypt dostarczony przez @Yaroslav i scenariusza przewidzianego przez @Quandary więc teraz pracuje dla baz danych, których zapytania SQL upuścić wszystkie klucze obce jeden o jeden przekracza rozmiar przypisany do zmiennej SQL (lub MAX).

Zmienione skrypty upuszczają 5 kluczy obcych w iteracji (aby być bezpiecznym, zaimplementowano dodając TOP 5). Skrypty zatrzymują się, gdy nie ma już żadnego klucza obcego (zmienna SQL pozostaje pusta po uruchomieniu SELECT).

Pierwszy scenariusz @Yaroslav

DECLARE @SQL varchar(4000) 
IterationStart: 
SET @SQL='' 
SELECT TOP 5 @SQL = @SQL + 'ALTER TABLE ' + FK.TABLE_NAME + ' DROP CONSTRAINT [' + RTRIM(C.CONSTRAINT_NAME) +'];' + CHAR(13) 
    FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C 
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK 
    ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME 
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK 
    ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME 
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU 
    ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME 
INNER JOIN (
      SELECT i1.TABLE_NAME, i2.COLUMN_NAME 
       FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1 
      INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 
       ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME 
      WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY' 
      ) PT 
    ON PT.TABLE_NAME = PK.TABLE_NAME 
IF @SQL <> '' 
BEGIN 
    EXEC(@SQL) 
    GOTO IterationStart 
END 

scenariusz @Quandary

DECLARE @sql nvarchar(MAX) 

IterationStart: 
SET @sql = '' 

SELECT TOP 5 @sql = @sql + 'ALTER TABLE ' + QUOTENAME(RC.CONSTRAINT_SCHEMA) 
    + '.' + QUOTENAME(KCU1.TABLE_NAME) 
    + ' DROP CONSTRAINT ' + QUOTENAME(rc.CONSTRAINT_NAME) + '; ' 
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS RC 

INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU1 
    ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG 
    AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA 
    AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME 

IF @SQL <> '' 
BEGIN 
    EXEC(@SQL) 
    GOTO IterationStart 
END 
1

użyłem rozwiązanie information_schema wspomniany przez @Yaroslav, ale miał zbyt wiele kluczy obcych stałe w mojej bazy danych, aby dopasować je wszystko w varchar (MAX). Musiałem więc zmodyfikować skrypt, aby zamiast niego używał tabeli tymczasowej i kursora.

Dodałem także [] wokół nazwy tabeli.

DECLARE @SQL TABLE (Command VARCHAR(MAX)) 

INSERT @SQL 
SELECT 'ALTER TABLE [' + FK.TABLE_NAME + '] DROP CONSTRAINT [' + RTRIM(C.CONSTRAINT_NAME) +'];' + CHAR(13) 
    FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS C 
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS FK 
    ON C.CONSTRAINT_NAME = FK.CONSTRAINT_NAME 
INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK 
    ON C.UNIQUE_CONSTRAINT_NAME = PK.CONSTRAINT_NAME 
INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE CU 
    ON C.CONSTRAINT_NAME = CU.CONSTRAINT_NAME 
INNER JOIN (
      SELECT i1.TABLE_NAME, i2.COLUMN_NAME 
       FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS i1 
      INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE i2 
       ON i1.CONSTRAINT_NAME = i2.CONSTRAINT_NAME 
      WHERE i1.CONSTRAINT_TYPE = 'PRIMARY KEY' 
      ) PT 
    ON PT.TABLE_NAME = PK.TABLE_NAME 

DECLARE cmdCursor CURSOR 
    FOR SELECT Command FROM @SQL 
OPEN cmdCursor 
DECLARE @Command VARCHAR(MAX) 

FETCH NEXT FROM cmdCursor INTO @Command 
WHILE @@FETCH_STATUS = 0 
BEGIN 
    PRINT @Command 
    EXEC (@Command) 
    FETCH NEXT FROM cmdCursor INTO @Command 
END 

CLOSE cmdCursor; 
DEALLOCATE cmdCursor; 
1
CREATE TABLE #Commands (Command VARCHAR(MAX)) 

INSERT #Commands 
SELECT 'ALTER TABLE ' + QUOTENAME(RC.CONSTRAINT_SCHEMA) 
    + '.' + QUOTENAME(KCU1.TABLE_NAME) 
    + ' DROP CONSTRAINT ' + QUOTENAME(rc.CONSTRAINT_NAME) + '; ' 
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS AS RC 

INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU1 
    ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG 
    AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA 
    AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME 
WHERE ORDINAL_POSITION=1 

--SELECT * FROM #Commands 

DECLARE @Command VARCHAR(MAX) 
DECLARE curCommand CURSOR FOR 
SELECT Command FROM #Commands 

OPEN curCommand 

FETCH NEXT FROM curCommand INTO @Command 

WHILE @@FETCH_STATUS =0 
BEGIN 

    EXEC(@Command) 
    FETCH NEXT FROM curCommand INTO @Command 

END 

CLOSE curCommand 
DEALLOCATE curCommand 

DROP TABLE #Commands 
+2

Podczas gdy ten kod może odpowiedzieć na pytanie, podanie dodatkowego kontekstu dotyczącego tego, dlaczego i/lub jak ten kod odpowiada na pytanie, poprawia jego długoterminową wartość. – NathanOliver

+0

W przypadku baz danych z wieloma ograniczeniami inne wymienione rozwiązania nie powiodły się, ale ten działał dla mnie. – JCallico

2

Oto krótkie i słodkie skrypt używam (na SQL Server 2008 iw górę), aby usunąć wszystkie obce klucze, które również biorą pod uwagę schemat obiektu:

declare @sql varchar(max) = (
    select 
     'alter table ' + quotename(schema_name(schema_id)) + '.' + 
     quotename(object_name(parent_object_id)) + 
     ' drop constraint '+quotename(name) + ';' 
    from sys.foreign_keys 
    for xml path('') 
); 
exec sp_executesql @sql; 
+0

Świetna mała sztuczka! – Takarii

Powiązane problemy