Jak można tymczasowo wyłączyć ograniczenia klucza obcego za pomocą T-SQL?

823

Czy wyłączanie i włączanie ograniczeń kluczy obcych jest obsługiwane w SQL Server? A może moja jedyna opcja, dropa następnie ponownecreate ograniczenie?

Promień
źródło
127
Dla osób pytających „dlaczego” chciałbym to zrobić: jest to środowisko testowe, w którym chcę mieć możliwość usuwania i ładowania danych testowych z wielu tabel bez konieczności utrzymywania i określania kolejności ładowania danych. Integralność danych nie jest tak ważna w tym scenariuszu.
Ray
8
Uwaga - jeśli chcesz TRUNCATE tabeli, musisz faktycznie usunąć ograniczenia.
OutstandingBill
@OutstandingBill Najwyraźniej działa to na TRUNCATE .
jpaugh
1
Wydaje się dziwne, że ktoś kwestionuje to w środowisku produkcyjnym . Bardzo częstym przypadkiem użycia są wkładki luzem. Jeśli masz tabelę autoreferencyjną, czasem bardzo trudno jest posortować wstawkę zbiorczą, tak że wiersz nadrzędny jest zawsze wstawiany przed dzieckiem, więc wyłączasz ograniczenie, wstawianie zbiorcze, włączasz ograniczenie.
Auspex

Odpowiedzi:

1125

Jeśli chcesz wyłączyć wszystkie ograniczenia w bazie danych, po prostu uruchom ten kod:

-- disable all constraints
EXEC sp_MSforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"

Aby je ponownie włączyć, uruchom: (wydruk jest oczywiście opcjonalny i zawiera tylko tabele)

-- enable all constraints
exec sp_MSforeachtable @command1="print '?'", @command2="ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"

Uważam, że jest to przydatne podczas zapełniania danych z jednej bazy danych do drugiej. Jest to znacznie lepsze podejście niż usuwanie ograniczeń. Jak wspomniałeś, przydaje się przy upuszczaniu wszystkich danych w bazie danych i ponownym ich wypełnianiu (powiedzmy w środowisku testowym).

Jeśli usuwasz wszystkie dane, to rozwiązanie może okazać się pomocne.

Czasami przydaje się również wyłączenie wszystkich wyzwalaczy, możesz zobaczyć pełne rozwiązanie tutaj .

kristof
źródło
9
"ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"Czy powinien tam być tylko jeden „SPRAWDŹ”?
CrazyPyro
27
@CrazyPyro - nie potrzebujesz obu
Kristof
32
@CrazyPyro: oba są rzeczywiście potrzebne, dlatego, że pierwszy KONTROLA należy do Z, a drugi Z KONTRAKTEM (jest to rodzaj ograniczenia). Pierwsze sprawdzenie zapewnia sprawdzenie spójności danych podczas aktywacji ograniczenia. Jeśli tego nie chcesz, możesz napisać Z NOCHECK. Może być przydatny w niektórych sytuacjach testowych, gdy nie przejmujesz się rzeczywistymi danymi, o ile są takie, aby twoje zapytania miały coś do zabawy.
Valentino Vranken
8
Czy to źle, że otrzymuję ten wynik przy drugim poleceniu? „Instrukcja ALTER TABLE kolidowała z ograniczeniem KLUCZ ZAGRANICZNY ...”
Adwokat Diabła
34
Warto zauważyć, że nawet przy wyłączonych ograniczeniach TRUNCATE TABLE nie będzie działać. W tym celu musisz usunąć ograniczenia. W przeciwnym razie użyj USUŃ Z, ale weź pod uwagę różnicę: mssqltips.com/sqlservertip/1080/…
James McCormack,
404

http://www.sqljunkies.com/WebLog/roman/archive/2005/01/30/7037.aspx

-- Disable all table constraints

ALTER TABLE MyTable NOCHECK CONSTRAINT ALL

-- Enable all table constraints

ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT ALL

-- Disable single constraint

ALTER TABLE MyTable NOCHECK CONSTRAINT MyConstraint

-- Enable single constraint

ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT MyConstraint
ScottStonehouse
źródło
29
dobre znalezisko, ale pamiętaj, że nadal nie można obciąć tabeli bez usunięcia ograniczeń klucza obcego
Steven A. Lowe
1
musisz także pamiętać, że po ponownym włączeniu ograniczeń i sprawdzeniu integralności danych dane mogą się nie powieść, a naprawienie takiego problemu może być koszmarem, jeśli brakujące dane znajdują się na końcu długiego ciągu powiązane ograniczenia.
Jimoc
1
Potrzebujesz także drugiej kontroli przy ponownym włączaniu ograniczeń. W przeciwnym razie kod zostanie sprawdzony tylko raz, a nie włączony.
ps2goat,
3
tak, w 2012 r. potrzebne było „z czekiem”. Edycja została odrzucona? MS Link
crokusek
1
W instrukcjach użytych tutaj w celu ponownego włączenia ograniczeń brakuje klauzul WITH WITH CHECK. To dość poważna wada. Zobacz szczegóły mojej odpowiedzi - stackoverflow.com/a/35427150/81595 .
Scott Munro
37

Aby wyłączyć ograniczenie, masz ALTERtabelę za pomocą NOCHECK

ALTER TABLE [TABLE_NAME] NOCHECK CONSTRAINT [ALL|CONSTRAINT_NAME]

Aby umożliwić korzystanie z podwójnego SPRAWDŹ :

ALTER TABLE [TABLE_NAME] WITH CHECK CHECK CONSTRAINT [ALL|CONSTRAINT_NAME]
  • Podczas włączania należy zwrócić uwagę na podwójny SPRAWDŹ SPRAWDŹ SPRAWDŹ .
  • WSZYSTKIE oznaczają wszystkie ograniczenia w tabeli.

Po zakończeniu, jeśli chcesz sprawdzić status, użyj tego skryptu, aby wyświetlić stan ograniczenia. Będzie bardzo pomocny:

    SELECT (CASE 
        WHEN OBJECTPROPERTY(CONSTID, 'CNSTISDISABLED') = 0 THEN 'ENABLED'
        ELSE 'DISABLED'
        END) AS STATUS,
        OBJECT_NAME(CONSTID) AS CONSTRAINT_NAME,
        OBJECT_NAME(FKEYID) AS TABLE_NAME,
        COL_NAME(FKEYID, FKEY) AS COLUMN_NAME,
        OBJECT_NAME(RKEYID) AS REFERENCED_TABLE_NAME,
        COL_NAME(RKEYID, RKEY) AS REFERENCED_COLUMN_NAME
   FROM SYSFOREIGNKEYS
ORDER BY TABLE_NAME, CONSTRAINT_NAME,REFERENCED_TABLE_NAME, KEYNO 
Diego Mendes
źródło
Nie zobaczyć do kluczy podstawowych ? Dla kluczy obcych SYSFOREIGNKEYS Widok systemu sys.sysforeignkeys msdn.microsoft.com/en-us/library/ms177604.aspx
Kiquenet
jeśli próbujesz wyłączyć klucz podstawowy do wstawiania, zaleciłbym użycie (SET IDENTITY_INSERT), jeśli chcesz po prostu sprawdzić klucz podstawowy, możesz wypróbować sys.key_constraints z sys.indexes.is_primary_key
Diego Mendes
29

Najlepszą opcją jest DROP i TWORZENIE ograniczeń klucza obcego.

W tym poście nie znalazłem przykładów, które działałyby dla mnie „tak, jak są”, jeden nie działałby, gdyby klucze obce odwoływały się do różnych schematów, drugi nie działałby, gdyby klucze obce odnosiły się do wielu kolumn. Ten skrypt uwzględnia zarówno wiele schematów, jak i wiele kolumn na klucz obcy.

Oto skrypt, który generuje instrukcje „DODAJ OGRANICZENIE”, w przypadku wielu kolumn rozdzieli je przecinkami ( pamiętaj, aby zapisać dane wyjściowe przed wykonaniem instrukcji DROP ):

PRINT N'-- CREATE FOREIGN KEY CONSTRAINTS --';

SET NOCOUNT ON;
SELECT '
PRINT N''Creating '+ const.const_name +'...''
GO
ALTER TABLE ' + const.parent_obj + '
    ADD CONSTRAINT ' + const.const_name + ' FOREIGN KEY (
            ' + const.parent_col_csv + '
            ) REFERENCES ' + const.ref_obj + '(' + const.ref_col_csv + ')
GO'
FROM (
    SELECT QUOTENAME(fk.NAME) AS [const_name]
        ,QUOTENAME(schParent.NAME) + '.' + QUOTENAME(OBJECT_name(fkc.parent_object_id)) AS [parent_obj]
        ,STUFF((
                SELECT ',' + QUOTENAME(COL_NAME(fcP.parent_object_id, fcp.parent_column_id))
                FROM sys.foreign_key_columns AS fcP
                WHERE fcp.constraint_object_id = fk.object_id
                FOR XML path('')
                ), 1, 1, '') AS [parent_col_csv]
        ,QUOTENAME(schRef.NAME) + '.' + QUOTENAME(OBJECT_NAME(fkc.referenced_object_id)) AS [ref_obj]
        ,STUFF((
                SELECT ',' + QUOTENAME(COL_NAME(fcR.referenced_object_id, fcR.referenced_column_id))
                FROM sys.foreign_key_columns AS fcR
                WHERE fcR.constraint_object_id = fk.object_id
                FOR XML path('')
                ), 1, 1, '') AS [ref_col_csv]
    FROM sys.foreign_key_columns AS fkc
    INNER JOIN sys.foreign_keys AS fk ON fk.object_id = fkc.constraint_object_id
    INNER JOIN sys.objects AS oParent ON oParent.object_id = fkc.parent_object_id
    INNER JOIN sys.schemas AS schParent ON schParent.schema_id = oParent.schema_id
    INNER JOIN sys.objects AS oRef ON oRef.object_id = fkc.referenced_object_id
    INNER JOIN sys.schemas AS schRef ON schRef.schema_id = oRef.schema_id
    GROUP BY fkc.parent_object_id
        ,fkc.referenced_object_id
        ,fk.NAME
        ,fk.object_id
        ,schParent.NAME
        ,schRef.NAME
    ) AS const
ORDER BY const.const_name

Oto skrypt, który generuje instrukcje „DROP CONSTRAINT”:

PRINT N'-- DROP FOREIGN KEY CONSTRAINTS --';

SET NOCOUNT ON;

SELECT '
PRINT N''Dropping ' + fk.NAME + '...''
GO
ALTER TABLE [' + sch.NAME + '].[' + OBJECT_NAME(fk.parent_object_id) + ']' + ' DROP  CONSTRAINT ' + '[' + fk.NAME + ']
GO'
FROM sys.foreign_keys AS fk
INNER JOIN sys.schemas AS sch ON sch.schema_id = fk.schema_id
ORDER BY fk.NAME
ofiara
źródło
5
Czy możesz wyjaśnić, dlaczego jest to lepsze niż wyłączanie i ponowne włączanie ograniczeń?
Mahmoodvcs
Niezły scenariusz. Podobne, ale alternatywne podejście można znaleźć na stronie: mssqltips.com/sqlservertip/3347/...
Matt Browne,
11

Standard SQL-92 pozwala deklarować constaint jako DEFERRABLE, dzięki czemu można go odroczyć (pośrednio lub jawnie) w ramach transakcji. Niestety, SQL Server nadal nie ma tej funkcji SQL-92.

Dla mnie zmiana ograniczenia na NOCHECK jest podobna do zmiany struktury bazy danych w locie - usunięcie ograniczeń z pewnością jest - i coś, czego należy unikać (np. Użytkownicy wymagają zwiększonych uprawnień).

oneedaywhen
źródło
11
   --Drop and Recreate Foreign Key Constraints

SET NOCOUNT ON

DECLARE @table TABLE(
   RowId INT PRIMARY KEY IDENTITY(1, 1),
   ForeignKeyConstraintName NVARCHAR(200),
   ForeignKeyConstraintTableSchema NVARCHAR(200),
   ForeignKeyConstraintTableName NVARCHAR(200),
   ForeignKeyConstraintColumnName NVARCHAR(200),
   PrimaryKeyConstraintName NVARCHAR(200),
   PrimaryKeyConstraintTableSchema NVARCHAR(200),
   PrimaryKeyConstraintTableName NVARCHAR(200),
   PrimaryKeyConstraintColumnName NVARCHAR(200)    
)

INSERT INTO @table(ForeignKeyConstraintName, ForeignKeyConstraintTableSchema, ForeignKeyConstraintTableName, ForeignKeyConstraintColumnName)
SELECT 
   U.CONSTRAINT_NAME, 
   U.TABLE_SCHEMA, 
   U.TABLE_NAME, 
   U.COLUMN_NAME 
FROM 
   INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
      INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
         ON U.CONSTRAINT_NAME = C.CONSTRAINT_NAME
WHERE
   C.CONSTRAINT_TYPE = 'FOREIGN KEY'

UPDATE @table SET
   PrimaryKeyConstraintName = UNIQUE_CONSTRAINT_NAME
FROM 
   @table T
      INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R
         ON T.ForeignKeyConstraintName = R.CONSTRAINT_NAME

UPDATE @table SET
   PrimaryKeyConstraintTableSchema  = TABLE_SCHEMA,
   PrimaryKeyConstraintTableName  = TABLE_NAME
FROM @table T
   INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
      ON T.PrimaryKeyConstraintName = C.CONSTRAINT_NAME

UPDATE @table SET
   PrimaryKeyConstraintColumnName = COLUMN_NAME
FROM @table T
   INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
      ON T.PrimaryKeyConstraintName = U.CONSTRAINT_NAME

--SELECT * FROM @table

--DROP CONSTRAINT:
SELECT
   '
   ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + '] 
   DROP CONSTRAINT ' + ForeignKeyConstraintName + '

   GO'
FROM
   @table

--ADD CONSTRAINT:
SELECT
   '
   ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + '] 
   ADD CONSTRAINT ' + ForeignKeyConstraintName + ' FOREIGN KEY(' + ForeignKeyConstraintColumnName + ') REFERENCES [' + PrimaryKeyConstraintTableSchema + '].[' + PrimaryKeyConstraintTableName + '](' + PrimaryKeyConstraintColumnName + ')

   GO'
FROM
   @table

GO

Zgadzam się z tobą, Hamlin. Gdy przesyłasz dane za pomocą SSIS lub chcesz replikować dane, wydaje się, że konieczne jest tymczasowe wyłączenie lub usunięcie ograniczeń klucza obcego, a następnie ponowne ich włączenie lub odtworzenie. W takich przypadkach integralność referencyjna nie stanowi problemu, ponieważ jest już utrzymywana w źródłowej bazie danych. Dlatego możesz być spokojny w tej sprawie.

Amir Hussein Samiani
źródło
Ten skrypt jest świetny do generowania moich poleceń „ALTER”, ale jak mogę je uruchomić i uruchomić w SP?
BlueChippy
1
Myślę, że to nie zadziała, jeśli którykolwiek z kluczy obcych jest wielokolumnowy
Zar Shardan,
Nie wygenerowało to również wszystkich znaków dla zbyt długich nazw tabel / kluczy.
Joshua Drake
11
SET NOCOUNT ON

DECLARE @table TABLE(
   RowId INT PRIMARY KEY IDENTITY(1, 1),
   ForeignKeyConstraintName NVARCHAR(200),
   ForeignKeyConstraintTableSchema NVARCHAR(200),
   ForeignKeyConstraintTableName NVARCHAR(200),
   ForeignKeyConstraintColumnName NVARCHAR(200),
   PrimaryKeyConstraintName NVARCHAR(200),
   PrimaryKeyConstraintTableSchema NVARCHAR(200),
   PrimaryKeyConstraintTableName NVARCHAR(200),
   PrimaryKeyConstraintColumnName NVARCHAR(200),
   UpdateRule NVARCHAR(100),
   DeleteRule NVARCHAR(100)   
)

INSERT INTO @table(ForeignKeyConstraintName, ForeignKeyConstraintTableSchema, ForeignKeyConstraintTableName, ForeignKeyConstraintColumnName)
SELECT 
   U.CONSTRAINT_NAME, 
   U.TABLE_SCHEMA, 
   U.TABLE_NAME, 
   U.COLUMN_NAME
FROM 
   INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
      INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
         ON U.CONSTRAINT_NAME = C.CONSTRAINT_NAME
WHERE
   C.CONSTRAINT_TYPE = 'FOREIGN KEY'

UPDATE @table SET
   T.PrimaryKeyConstraintName = R.UNIQUE_CONSTRAINT_NAME,
   T.UpdateRule = R.UPDATE_RULE,
   T.DeleteRule = R.DELETE_RULE
FROM 
   @table T
      INNER JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS R
         ON T.ForeignKeyConstraintName = R.CONSTRAINT_NAME

UPDATE @table SET
   PrimaryKeyConstraintTableSchema  = TABLE_SCHEMA,
   PrimaryKeyConstraintTableName  = TABLE_NAME
FROM @table T
   INNER JOIN INFORMATION_SCHEMA.TABLE_CONSTRAINTS C
      ON T.PrimaryKeyConstraintName = C.CONSTRAINT_NAME

UPDATE @table SET
   PrimaryKeyConstraintColumnName = COLUMN_NAME
FROM @table T
   INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE U
      ON T.PrimaryKeyConstraintName = U.CONSTRAINT_NAME

--SELECT * FROM @table

SELECT '
BEGIN TRANSACTION
BEGIN TRY'

--DROP CONSTRAINT:
SELECT
   '
 ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + '] 
 DROP CONSTRAINT ' + ForeignKeyConstraintName + '
   '
FROM
   @table

SELECT '
END TRY

BEGIN CATCH
   ROLLBACK TRANSACTION
   RAISERROR(''Operation failed.'', 16, 1)
END CATCH

IF(@@TRANCOUNT != 0)
BEGIN
   COMMIT TRANSACTION
   RAISERROR(''Operation completed successfully.'', 10, 1)
END
'

--ADD CONSTRAINT:
SELECT '
BEGIN TRANSACTION
BEGIN TRY'

SELECT
   '
   ALTER TABLE [' + ForeignKeyConstraintTableSchema + '].[' + ForeignKeyConstraintTableName + '] 
   ADD CONSTRAINT ' + ForeignKeyConstraintName + ' FOREIGN KEY(' + ForeignKeyConstraintColumnName + ') REFERENCES [' + PrimaryKeyConstraintTableSchema + '].[' + PrimaryKeyConstraintTableName + '](' + PrimaryKeyConstraintColumnName + ') ON UPDATE ' + UpdateRule + ' ON DELETE ' + DeleteRule + '
   '
FROM
   @table

SELECT '
END TRY

BEGIN CATCH
   ROLLBACK TRANSACTION
   RAISERROR(''Operation failed.'', 16, 1)
END CATCH

IF(@@TRANCOUNT != 0)
BEGIN
   COMMIT TRANSACTION
   RAISERROR(''Operation completed successfully.'', 10, 1)
END'

GO
Amir Hussein Samiani
źródło
10

WITH CHECK CHECK jest prawie na pewno wymagane!

Ten punkt został poruszony w niektórych odpowiedziach i komentarzach, ale uważam, że wystarczająco ważne jest, aby ponownie go przywołać.

Ponowne włączenie ograniczenia za pomocą następującego polecenia (nie WITH CHECK) będzie miało poważne wady .

ALTER TABLE MyTable CHECK CONSTRAINT MyConstraint;

Z KONTROLĄ | Z NOCHECK

Określa, czy dane w tabeli są sprawdzane pod kątem nowo dodanego lub ponownie włączonego klucza OBCEGO lub ograniczenia SPRAWDŹ. Jeśli nie zostanie określony, dla nowych ograniczeń zakłada się Z KONTROLĄ, a dla wiązań ponownie włączonych - Z ZAKŁADEM.

Jeśli nie chcesz weryfikować nowych ograniczeń KLUCZ KONTROLNY lub OBYWATELI względem istniejących danych, użyj opcji Z NOCHECK. Nie zalecamy tego, z wyjątkiem rzadkich przypadków. Nowe ograniczenie zostanie ocenione we wszystkich późniejszych aktualizacjach danych. Wszelkie naruszenia ograniczeń, które są pomijane przez WITH NOCHECK po dodaniu ograniczenia, mogą spowodować niepowodzenie przyszłych aktualizacji, jeśli zaktualizują wiersze danymi, które nie są zgodne z ograniczeniem.

Optymalizator kwerendy nie uwzględnia ograniczeń zdefiniowanych za pomocą NOCHECK. Takie ograniczenia są ignorowane, dopóki nie zostaną ponownie włączone przy użyciu tabeli ALTER TABLE Z KONTROLĄ KONTRAKTU WSZYSTKIEGO.

Uwaga: Z NOCHECK domyślnie włączane są ograniczenia. Muszę się zastanawiać, dlaczego ...

  1. Żadne istniejące dane w tabeli nie będą oceniane podczas wykonywania tego polecenia - pomyślne zakończenie nie gwarantuje, że dane w tabeli są prawidłowe zgodnie z ograniczeniem.
  2. Podczas następnej aktualizacji niepoprawnych rekordów ograniczenie zostanie ocenione i zakończy się niepowodzeniem, co spowoduje błędy, które mogą być niezwiązane z faktyczną aktualizacją.
  3. Logika aplikacji, która polega na ograniczeniu w celu zapewnienia poprawności danych, może się nie powieść.
  4. Optymalizator zapytań nie będzie korzystał z żadnego ograniczenia, które jest włączone w ten sposób.

Sys.foreign_keys widok System zapewnia pewien wgląd w tej kwestii. Pamiętaj, że ma zarówno kolumnę, jak is_disabledi is_not_trustedkolumnę. is_disabledwskazuje, czy przyszłe operacje manipulacji danymi zostaną sprawdzone pod kątem ograniczenia. is_not_trustedwskazuje, czy wszystkie dane aktualnie zawarte w tabeli zostały sprawdzone pod kątem ograniczenia.

ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT MyConstraint;

Czy Twoim ograniczeniom można zaufać? Odkryć...

SELECT * FROM sys.foreign_keys WHERE is_not_trusted = 1;
Scott Munro
źródło
9

Pierwszy post :)

W przypadku OP rozwiązanie kristof będzie działać, chyba że wystąpią problemy z ogromnymi problemami z danymi i balonami dziennika transakcji przy dużych usunięciach. Ponadto, nawet z zapasem pamięci tlog, ponieważ usunięcie zapisu do tlogu, operacja może zająć BARDZO długi czas dla tabel z setkami milionów wierszy.

Używam serii kursorów, aby często obcinać i ponownie ładować duże kopie jednej z naszych ogromnych produkcyjnych baz danych. Opracowane rozwiązanie uwzględnia wiele schematów, wiele kolumn kluczy obcych, a co najważniejsze, można je wyodrębnić do użycia w SSIS.

Polega ona na utworzeniu trzech tabel pomostowych (prawdziwych tabel) do przechowywania skryptów DROP, CREATE i CHECK FK, tworzeniu i wstawianiu tych skryptów do tabel, a następnie zapętlaniu tabel i wykonywaniu ich. Załączony skrypt składa się z czterech części: 1.) tworzenie i przechowywanie skryptów w trzech tabelach pomostowych (rzeczywistych), 2.) wykonywanie skryptów upuszczania FK za pomocą kursora jeden po drugim, 3.) Używanie sp_MSforeachtable do obcinania wszystkich tabele w bazie danych inne niż nasze trzy tabele pomostowe oraz 4.) wykonywanie skryptów FK i sprawdzanie skryptów FK na końcu pakietu ETL SSIS.

Uruchom część dotyczącą tworzenia skryptu w zadaniu Wykonaj SQL w SSIS. Uruchom część „Wykonaj Skrypty FK” w drugim zadaniu Wykonaj SQL. Umieść skrypt obcinania w trzecim zadaniu Wykonaj SQL, a następnie wykonaj wszystkie inne procesy ETL, które musisz wykonać przed dołączeniem skryptów UTWÓRZ i SPRAWDŹ w końcowym zadaniu Wykonaj SQL (lub dwa w razie potrzeby) na końcu przepływu sterowania.

Przechowywanie skryptów w prawdziwych tabelach okazało się nieocenione, gdy ponowne zastosowanie kluczy obcych kończy się niepowodzeniem, ponieważ możesz wybrać * z sync_CreateFK, skopiować / wkleić do okna zapytania, uruchamiać je pojedynczo i naprawić problemy z danymi po znajdź te, które się nie powiodły / nadal nie mogą ponownie złożyć wniosku.

Nie uruchamiaj ponownie skryptu ponownie, jeśli się nie powiedzie, nie upewniając się, że ponownie zastosujesz wszystkie klucze / kontrole obce przed tym, lub najprawdopodobniej stracisz trochę tworzenia i sprawdzisz skrypty fk, gdy nasze tabele pomostowe zostaną upuszczone i odtworzone przed utworzeniem skryptów do wykonania.

----------------------------------------------------------------------------
1)
/*
Author:         Denmach
DateCreated:    2014-04-23
Purpose:        Generates SQL statements to DROP, ADD, and CHECK existing constraints for a 
                database.  Stores scripts in tables on target database for execution.  Executes
                those stored scripts via independent cursors. 
DateModified:
ModifiedBy
Comments:       This will eliminate deletes and the T-log ballooning associated with it.
*/

DECLARE @schema_name SYSNAME; 
DECLARE @table_name SYSNAME; 
DECLARE @constraint_name SYSNAME; 
DECLARE @constraint_object_id INT; 
DECLARE @referenced_object_name SYSNAME; 
DECLARE @is_disabled BIT; 
DECLARE @is_not_for_replication BIT; 
DECLARE @is_not_trusted BIT; 
DECLARE @delete_referential_action TINYINT; 
DECLARE @update_referential_action TINYINT; 
DECLARE @tsql NVARCHAR(4000); 
DECLARE @tsql2 NVARCHAR(4000); 
DECLARE @fkCol SYSNAME; 
DECLARE @pkCol SYSNAME; 
DECLARE @col1 BIT; 
DECLARE @action CHAR(6);  
DECLARE @referenced_schema_name SYSNAME;



--------------------------------Generate scripts to drop all foreign keys in a database --------------------------------

IF OBJECT_ID('dbo.sync_dropFK') IS NOT NULL
DROP TABLE sync_dropFK

CREATE TABLE sync_dropFK
    (
    ID INT IDENTITY (1,1) NOT NULL
    , Script NVARCHAR(4000)
    )

DECLARE FKcursor CURSOR FOR

    SELECT 
        OBJECT_SCHEMA_NAME(parent_object_id)
        , OBJECT_NAME(parent_object_id)
        , name
    FROM 
        sys.foreign_keys WITH (NOLOCK)
    ORDER BY 
        1,2;

OPEN FKcursor;

FETCH NEXT FROM FKcursor INTO 
    @schema_name
    , @table_name
    , @constraint_name

WHILE @@FETCH_STATUS = 0

BEGIN
        SET @tsql = 'ALTER TABLE '
                + QUOTENAME(@schema_name) 
                + '.' 
                + QUOTENAME(@table_name)
                + ' DROP CONSTRAINT ' 
                + QUOTENAME(@constraint_name) 
                + ';';
    --PRINT @tsql;
    INSERT sync_dropFK  (
                        Script
                        )
                        VALUES (
                                @tsql
                                )   

    FETCH NEXT FROM FKcursor INTO 
    @schema_name
    , @table_name
    , @constraint_name
    ;

END;

CLOSE FKcursor;

DEALLOCATE FKcursor;


---------------Generate scripts to create all existing foreign keys in a database --------------------------------
----------------------------------------------------------------------------------------------------------
IF OBJECT_ID('dbo.sync_createFK') IS NOT NULL
DROP TABLE sync_createFK

CREATE TABLE sync_createFK
    (
    ID INT IDENTITY (1,1) NOT NULL
    , Script NVARCHAR(4000)
    )

IF OBJECT_ID('dbo.sync_createCHECK') IS NOT NULL
DROP TABLE sync_createCHECK

CREATE TABLE sync_createCHECK
    (
    ID INT IDENTITY (1,1) NOT NULL
    , Script NVARCHAR(4000)
    )   

DECLARE FKcursor CURSOR FOR

     SELECT 
        OBJECT_SCHEMA_NAME(parent_object_id)
        , OBJECT_NAME(parent_object_id)
        , name
        , OBJECT_NAME(referenced_object_id)
        , OBJECT_ID
        , is_disabled
        , is_not_for_replication
        , is_not_trusted
        , delete_referential_action
        , update_referential_action
        , OBJECT_SCHEMA_NAME(referenced_object_id)

    FROM 
        sys.foreign_keys WITH (NOLOCK)

    ORDER BY 
        1,2;

OPEN FKcursor;

FETCH NEXT FROM FKcursor INTO 
    @schema_name
    , @table_name
    , @constraint_name
    , @referenced_object_name
    , @constraint_object_id
    , @is_disabled
    , @is_not_for_replication
    , @is_not_trusted
    , @delete_referential_action
    , @update_referential_action
    , @referenced_schema_name;

WHILE @@FETCH_STATUS = 0

BEGIN

        BEGIN
            SET @tsql = 'ALTER TABLE '
                        + QUOTENAME(@schema_name) 
                        + '.' 
                        + QUOTENAME(@table_name)
                        +   CASE 
                                @is_not_trusted
                                WHEN 0 THEN ' WITH CHECK '
                                ELSE ' WITH NOCHECK '
                            END
                        + ' ADD CONSTRAINT ' 
                        + QUOTENAME(@constraint_name)
                        + ' FOREIGN KEY (';

        SET @tsql2 = '';

        DECLARE ColumnCursor CURSOR FOR 

            SELECT 
                COL_NAME(fk.parent_object_id
                , fkc.parent_column_id)
                , COL_NAME(fk.referenced_object_id
                , fkc.referenced_column_id)

            FROM 
                sys.foreign_keys fk WITH (NOLOCK)
                INNER JOIN sys.foreign_key_columns fkc WITH (NOLOCK) ON fk.[object_id] = fkc.constraint_object_id

            WHERE 
                fkc.constraint_object_id = @constraint_object_id

            ORDER BY 
                fkc.constraint_column_id;

        OPEN ColumnCursor;

        SET @col1 = 1;

        FETCH NEXT FROM ColumnCursor INTO @fkCol, @pkCol;

        WHILE @@FETCH_STATUS = 0

        BEGIN
            IF (@col1 = 1)
                SET @col1 = 0;
            ELSE
            BEGIN
                SET @tsql = @tsql + ',';
                SET @tsql2 = @tsql2 + ',';
            END;

            SET @tsql = @tsql + QUOTENAME(@fkCol);
            SET @tsql2 = @tsql2 + QUOTENAME(@pkCol);
            --PRINT '@tsql = ' + @tsql 
            --PRINT '@tsql2 = ' + @tsql2
            FETCH NEXT FROM ColumnCursor INTO @fkCol, @pkCol;
            --PRINT 'FK Column ' + @fkCol
            --PRINT 'PK Column ' + @pkCol 
        END;

        CLOSE ColumnCursor;
        DEALLOCATE ColumnCursor;

        SET @tsql = @tsql + ' ) REFERENCES ' 
                    + QUOTENAME(@referenced_schema_name) 
                    + '.' 
                    + QUOTENAME(@referenced_object_name)
                    + ' (' 
                    + @tsql2 + ')';

        SET @tsql = @tsql
                    + ' ON UPDATE ' 
                    + 
                        CASE @update_referential_action
                            WHEN 0 THEN 'NO ACTION '
                            WHEN 1 THEN 'CASCADE '
                            WHEN 2 THEN 'SET NULL '
                                ELSE 'SET DEFAULT '
                        END

                    + ' ON DELETE ' 
                    + 
                        CASE @delete_referential_action
                            WHEN 0 THEN 'NO ACTION '
                            WHEN 1 THEN 'CASCADE '
                            WHEN 2 THEN 'SET NULL '
                                ELSE 'SET DEFAULT '
                        END

                    + 
                    CASE @is_not_for_replication
                        WHEN 1 THEN ' NOT FOR REPLICATION '
                            ELSE ''
                    END
                    + ';';

        END;

    --  PRINT @tsql
        INSERT sync_createFK    
                        (
                        Script
                        )
                        VALUES (
                                @tsql
                                )

-------------------Generate CHECK CONSTRAINT scripts for a database ------------------------------
----------------------------------------------------------------------------------------------------------

        BEGIN

        SET @tsql = 'ALTER TABLE '
                    + QUOTENAME(@schema_name) 
                    + '.' 
                    + QUOTENAME(@table_name)
                    + 
                        CASE @is_disabled
                            WHEN 0 THEN ' CHECK '
                                ELSE ' NOCHECK '
                        END
                    + 'CONSTRAINT ' 
                    + QUOTENAME(@constraint_name)
                    + ';';
        --PRINT @tsql;
        INSERT sync_createCHECK 
                        (
                        Script
                        )
                        VALUES (
                                @tsql
                                )   
        END;

    FETCH NEXT FROM FKcursor INTO 
    @schema_name
    , @table_name
    , @constraint_name
    , @referenced_object_name
    , @constraint_object_id
    , @is_disabled
    , @is_not_for_replication
    , @is_not_trusted
    , @delete_referential_action
    , @update_referential_action
    , @referenced_schema_name;

END;

CLOSE FKcursor;

DEALLOCATE FKcursor;

--SELECT * FROM sync_DropFK
--SELECT * FROM sync_CreateFK
--SELECT * FROM sync_CreateCHECK
---------------------------------------------------------------------------
2.)
-----------------------------------------------------------------------------------------------------------------
----------------------------execute Drop FK Scripts --------------------------------------------------

DECLARE @scriptD NVARCHAR(4000)

DECLARE DropFKCursor CURSOR FOR
    SELECT Script 
    FROM sync_dropFK WITH (NOLOCK)

OPEN DropFKCursor

FETCH NEXT FROM DropFKCursor
INTO @scriptD

WHILE @@FETCH_STATUS = 0
BEGIN
--PRINT @scriptD
EXEC (@scriptD)
FETCH NEXT FROM DropFKCursor
INTO @scriptD
END
CLOSE DropFKCursor
DEALLOCATE DropFKCursor
--------------------------------------------------------------------------------
3.) 

------------------------------------------------------------------------------------------------------------------
----------------------------Truncate all tables in the database other than our staging tables --------------------
------------------------------------------------------------------------------------------------------------------


EXEC sp_MSforeachtable 'IF OBJECT_ID(''?'') NOT IN 
(
ISNULL(OBJECT_ID(''dbo.sync_createCHECK''),0),
ISNULL(OBJECT_ID(''dbo.sync_createFK''),0),
ISNULL(OBJECT_ID(''dbo.sync_dropFK''),0)
)
BEGIN TRY
 TRUNCATE TABLE ?
END TRY
BEGIN CATCH
 PRINT ''Truncation failed on''+ ? +''
END CATCH;' 
GO
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------
----------------------------execute Create FK Scripts and CHECK CONSTRAINT Scripts---------------
----------------------------tack me at the end of the ETL in a SQL task-------------------------
-------------------------------------------------------------------------------------------------
DECLARE @scriptC NVARCHAR(4000)

DECLARE CreateFKCursor CURSOR FOR
    SELECT Script 
    FROM sync_createFK WITH (NOLOCK)

OPEN CreateFKCursor

FETCH NEXT FROM CreateFKCursor
INTO @scriptC

WHILE @@FETCH_STATUS = 0
BEGIN
--PRINT @scriptC
EXEC (@scriptC)
FETCH NEXT FROM CreateFKCursor
INTO @scriptC
END
CLOSE CreateFKCursor
DEALLOCATE CreateFKCursor
-------------------------------------------------------------------------------------------------
DECLARE @scriptCh NVARCHAR(4000)

DECLARE CreateCHECKCursor CURSOR FOR
    SELECT Script 
    FROM sync_createCHECK WITH (NOLOCK)

OPEN CreateCHECKCursor

FETCH NEXT FROM CreateCHECKCursor
INTO @scriptCh

WHILE @@FETCH_STATUS = 0
BEGIN
--PRINT @scriptCh
EXEC (@scriptCh)
FETCH NEXT FROM CreateCHECKCursor
INTO @scriptCh
END
CLOSE CreateCHECKCursor
DEALLOCATE CreateCHECKCursor
Denmach
źródło
7

Znajdź ograniczenie

SELECT * 
FROM sys.foreign_keys
WHERE referenced_object_id = object_id('TABLE_NAME')

Uruchom SQL wygenerowany przez ten SQL

SELECT 
    'ALTER TABLE ' +  OBJECT_SCHEMA_NAME(parent_object_id) +
    '.[' + OBJECT_NAME(parent_object_id) + 
    '] DROP CONSTRAINT ' + name
FROM sys.foreign_keys
WHERE referenced_object_id = object_id('TABLE_NAME')

Bezpieczna droga.

Uwaga: Dodano rozwiązanie dla usuwania ograniczenia, aby tabela mogła zostać usunięta lub zmodyfikowana bez błędu wiązania.

Aditya
źródło
3

Kliknij prawym przyciskiem myszy projekt tabeli i przejdź do Relacje i wybierz klucz obcy w panelu po lewej stronie oraz w panelu po prawej stronie, ustaw opcję Wymuś ograniczenie klucza obcego na „Tak” (aby włączyć ograniczenia klucza obcego) lub „Nie” (aby wyłącz to). wprowadź opis zdjęcia tutaj

AmirHossein Manian
źródło
3

Odpowiedź oznaczona jako „905” wygląda dobrze, ale nie działa.

Obserwowanie działało dla mnie. Żaden klucz główny, klucz unikalny lub ograniczenia domyślne NIE mogą zostać wyłączone. W rzeczywistości, jeśli „sp_helpconstraint” pokazuje „n / a” w status_enabled - oznacza, że NIE można go włączyć / wyłączyć.

- Aby wygenerować skrypt do WYŁĄCZENIA

select 'ALTER TABLE ' + object_name(id) + ' NOCHECK CONSTRAINT [' + object_name(constid) + ']'
from sys.sysconstraints 
where status & 0x4813 = 0x813 order by object_name(id)

- Aby wygenerować skrypt do ENABLE

select 'ALTER TABLE ' + object_name(id) + ' CHECK CONSTRAINT [' + object_name(constid) + ']'
from sys.sysconstraints 
where status & 0x4813 = 0x813 order by object_name(id)
V. Agarwal
źródło
3

Powinieneś być w stanie wyłączyć ograniczenia klucza obcego w taki sam sposób, w jaki tymczasowo wyłączasz inne ograniczenia:

Alter table MyTable nocheck constraint FK_ForeignKeyConstraintName

Upewnij się tylko, że wyłączasz ograniczenie w pierwszej tabeli wymienionej w nazwie ograniczenia. Na przykład, jeśli moim ograniczeniem dla klucza obcego było FK_LocationsEmployeesLocationIdEmployeeId, chciałbym użyć następujących opcji:

Alter table Locations nocheck constraint FK_LocationsEmployeesLocationIdEmployeeId

nawet jeśli naruszenie tego ograniczenia spowoduje błąd, który niekoniecznie określa tę tabelę jako źródło konfliktu.

lwilliams
źródło
1

Jeden skrypt, który rządzi nimi wszystkimi: łączy to polecenia obcinania i usuwania ze sp_MSforeachtable, dzięki czemu można uniknąć upuszczania i odtwarzania ograniczeń - wystarczy określić tabele, które należy usunąć, a nie obcięte, a dla moich celów na dobre zawarłem dodatkowy filtr schematu miara (testowana w 2008r2)

declare @schema nvarchar(max) = 'and Schema_Id=Schema_id(''Value'')'
declare @deletiontables nvarchar(max) = '(''TableA'',''TableB'')'
declare @truncateclause nvarchar(max) = @schema + ' and o.Name not in ' +  + @deletiontables;
declare @deleteclause nvarchar(max) = @schema + ' and o.Name in ' + @deletiontables;        

exec sp_MSforeachtable 'alter table ? nocheck constraint all', @whereand=@schema
exec sp_MSforeachtable 'truncate table ?', @whereand=@truncateclause
exec sp_MSforeachtable 'delete from ?', @whereand=@deleteclause
exec sp_MSforeachtable 'alter table ? with check check constraint all', @whereand=@schema
Alex Hinton
źródło
1

Możesz tymczasowo wyłączyć ograniczenia w swoich tabelach, wykonać pracę, a następnie je odbudować.

Oto prosty sposób, aby to zrobić ...

Wyłącz wszystkie indeksy, w tym klucze podstawowe, co spowoduje wyłączenie wszystkich kluczy obcych, a następnie ponownie włącz tylko klucze podstawowe, abyś mógł z nimi pracować ...

DECLARE @sql AS NVARCHAR(max)=''
select @sql = @sql +
    'ALTER INDEX ALL ON [' + t.[name] + '] DISABLE;'+CHAR(13)
from  
    sys.tables t
where type='u'

select @sql = @sql +
    'ALTER INDEX ' + i.[name] + ' ON [' + t.[name] + '] REBUILD;'+CHAR(13)
from  
    sys.key_constraints i
join
    sys.tables t on i.parent_object_id=t.object_id
where
    i.type='PK'


exec dbo.sp_executesql @sql;
go

[Zrób coś, na przykład ładując dane]

Następnie ponownie włącz i odbuduj indeksy ...

DECLARE @sql AS NVARCHAR(max)=''
select @sql = @sql +
    'ALTER INDEX ALL ON [' + t.[name] + '] REBUILD;'+CHAR(13)
from  
    sys.tables t
where type='u'

exec dbo.sp_executesql @sql;
go
Carter Medlin
źródło
Wydawało się to obiecujące, ale @sqlzawsze zostaje obcięte. :(
Will Strohl,
1

Mam bardziej przydatną wersję, jeśli jesteś zainteresowany. Wyciągnąłem trochę kodu z witryny, na której link nie jest już aktywny. Zmodyfikowałem go, aby uwzględnić tablicę tabel w procedurze przechowywanej, i wypełnia upuszczanie, obcinanie, dodawanie instrukcji przed wykonaniem wszystkich. Dzięki temu masz kontrolę nad tym, które tabele wymagają obcinania.

/****** Object:  UserDefinedTableType [util].[typ_objects_for_managing]    Script Date: 03/04/2016 16:42:55 ******/
CREATE TYPE [util].[typ_objects_for_managing] AS TABLE(
    [schema] [sysname] NOT NULL,
    [object] [sysname] NOT NULL
)
GO

create procedure [util].[truncate_table_with_constraints]
@objects_for_managing util.typ_objects_for_managing readonly

--@schema sysname
--,@table sysname

as 
--select
--    @table = 'TABLE',
--    @schema = 'SCHEMA'

declare @exec_table as table (ordinal int identity (1,1), statement nvarchar(4000), primary key (ordinal));

--print '/*Drop Foreign Key Statements for ['+@schema+'].['+@table+']*/'

insert into @exec_table (statement)
select
          'ALTER TABLE ['+SCHEMA_NAME(o.schema_id)+'].['+ o.name+'] DROP CONSTRAINT ['+fk.name+']'
from sys.foreign_keys fk
inner join sys.objects o
          on fk.parent_object_id = o.object_id
where 
exists ( 
select * from @objects_for_managing chk 
where 
chk.[schema] = SCHEMA_NAME(o.schema_id)  
and 
chk.[object] = o.name
) 
;
          --o.name = @table and
          --SCHEMA_NAME(o.schema_id)  = @schema

insert into @exec_table (statement) 
select
'TRUNCATE TABLE ' + src.[schema] + '.' + src.[object] 
from @objects_for_managing src
; 

--print '/*Create Foreign Key Statements for ['+@schema+'].['+@table+']*/'
insert into @exec_table (statement)
select 'ALTER TABLE ['+SCHEMA_NAME(o.schema_id)+'].['+o.name+'] ADD CONSTRAINT ['+fk.name+'] FOREIGN KEY (['+c.name+']) 
REFERENCES ['+SCHEMA_NAME(refob.schema_id)+'].['+refob.name+'](['+refcol.name+'])'
from sys.foreign_key_columns fkc
inner join sys.foreign_keys fk
          on fkc.constraint_object_id = fk.object_id
inner join sys.objects o
          on fk.parent_object_id = o.object_id
inner join sys.columns c
          on      fkc.parent_column_id = c.column_id and
                   o.object_id = c.object_id
inner join sys.objects refob
          on fkc.referenced_object_id = refob.object_id
inner join sys.columns refcol
          on fkc.referenced_column_id = refcol.column_id and
                   fkc.referenced_object_id = refcol.object_id
where 
exists ( 
select * from @objects_for_managing chk 
where 
chk.[schema] = SCHEMA_NAME(o.schema_id)  
and 
chk.[object] = o.name
) 
;

          --o.name = @table and
          --SCHEMA_NAME(o.schema_id)  = @schema



declare @looper int , @total_records int, @sql_exec nvarchar(4000)

select @looper = 1, @total_records = count(*) from @exec_table; 

while @looper <= @total_records 
begin

select @sql_exec = (select statement from @exec_table where ordinal =@looper)
exec sp_executesql @sql_exec 
print @sql_exec 
set @looper = @looper + 1
end
Zak Willis
źródło
Martwy link w twojej odpowiedzi. Wskazuje na pusty artykuł.
Neolisk
Cześć, może istnieć martwy link, ale cały kod jest określony w części. Co jest z tym nie tak?
Zak Willis
Nic złego, ale prawdopodobnie powinieneś edytować swoją odpowiedź i usunąć martwy link.
Neolisk