Як обмеження іноземних ключів можуть бути тимчасово відключені за допомогою T-SQL?


824

Чи відключення та включення обмежень для зовнішніх ключів підтримується в SQL Server? Або мій єдиний варіант обмеження dropта зменшенняcreate обмежень?


127
Для людей, які запитують "чому", я хотів би це зробити. Це для тестового середовища, коли я хочу мати змогу видаляти та завантажувати тестові дані з декількох таблиць, не потребуючи підтримки та уточнення порядку завантаження даних. Цілісність даних не так важлива для цього сценарію.
Рей

8
Примітка - якщо ви хочете ТРУНКУВАТИ таблицю, вам потрібно буде фактично скинути обмеження.
Видатний рахунок

@OutstandingBill Мабуть, це працює для TRUNCATE .
jpaugh

1
Як не дивно, що хтось поставить під сумнів це у виробничих умовах. Дуже поширений випадок використання для об'ємних вставок. Якщо у вас є самореференційна таблиця, іноді вкрай важко сортувати об'ємну вставку, щоб батьківський рядок завжди вставлявся перед дитиною, тому ви вимикаєте обмеження, об'ємну вставку, включаєте обмеження.
Auspex

Відповіді:


1126

Якщо ви хочете відключити всі обмеження в базі даних, просто запустіть цей код:

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

Щоб їх знову ввімкнути, запустіть: (друк, звичайно, не є обов'язковим, а це просто перелік таблиць)

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

Мені здається корисним при заповненні даних з однієї бази даних в іншу. Це набагато кращий підхід, ніж скасування обмежень. Як ви вже згадували, це стає корисним при скиданні всіх даних у базу даних та перепопуляції (скажімо, у тестовому середовищі).

Якщо ви видаляєте всі дані, ви можете вважати це рішення корисним.

Також іноді зручно відключити всі тригери, а повне рішення можна переглянути тут .


9
"ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"Чи має бути там лише одна "ПЕРЕВІРКА"?
CrazyPyro

27
@CrazyPyro - вам не потрібно обоє
kristof

32
@ CrazyPyro: обоє справді потрібні, причина в тому, що перша ПЕРЕВІРКА належить З, а друга ПЕРЕВІРКА З ОБМЕЖЕННЯМ (це тип обмеження). Перша перевірка гарантує, що ваші дані перевіряються на відповідність при активації обмеження. Якщо ви цього не хочете, можете написати ЗНО. Може бути корисним у певних тестових ситуаціях, коли ви не дбаєте про фактичні дані, якщо є такі, щоб ваші запити мали з чим грати.
Валентино Вранкен

8
Чи погано, що я отримую цей результат у другій команді? "Заява ALTER TABLE суперечить обмеженню FOREIGN KEY ..."
Адвокат Чорта

34
Варто зазначити, що навіть з обмеженими обмеженнями, TRUNCATE TABLE не працюватиме. Для цього вам потрібно буде скасувати обмеження. В іншому випадку використовуйте DELETE FROM, але врахуйте різницю: mssqltips.com/sqlservertip/1080/…
Джеймс Маккормак

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

29
хороша знахідка, але зауважте, що ви все одно не можете врубати таблицю, не усунувши обмеження іноземних ключів
Стівен А. Лоу,

1
і вам також потрібно буде знати, що коли ви знову ввімкнете обмеження і зробите перевірку цілісності даних, ваші дані можуть вийти з ладу і виправити подібну проблему, може бути кошмаром, якщо дані, що не вдається, в кінці довгого рядка пов'язані обмеження.
Jimoc

1
Вам також потрібна друга перевірка при вмиканні обмежень. В іншому випадку ваш код буде перевіряти обмеження лише один раз, а не вмикати його.
ps2goat

3
так, "з чеком", необхідним на 2012 рік. Редагування відхилено? MS Link
crokusek

1
У висловлюваннях, які використовуються тут для повторного включення обмежень, відсутні пункти WITH CHECK. Це досить великий недолік. Детальну інформацію див. У моїй відповіді - stackoverflow.com/a/35427150/81595 .
Скотт Манро

37

Щоб відключити обмеження, у вас є ALTERтаблиця за допомогою NOCHECK

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

Щоб дозволити вам використовувати подвійну перевірку :

ALTER TABLE [TABLE_NAME] WITH CHECK CHECK CONSTRAINT [ALL|CONSTRAINT_NAME]
  • Зверніть увагу на подвійну перевірку ПЕРЕВІРКИ при включенні.
  • ВСЕ означає всі обмеження в таблиці.

Після завершення, якщо вам потрібно перевірити стан, використовуйте цей скрипт, щоб перелічити стан обмеження. Буде дуже корисно:

    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 

Чи не дивитися на клавіші Primary ? Для зовнішніх ключів SYSFOREIGNKEYS Перегляд системи sys.sysforeignkeys msdn.microsoft.com/en-us/library/ms177604.aspx
Kiquenet

якщо ви намагаєтесь відключити первинний ключ для вставки, я б рекомендував використовувати (SET IDENTITY_INSERT), якщо ви хочете просто перевірити первинний ключ, ви можете спробувати sys.key_constraints з sys.indexes.is_primary_key
Дієго Мендес

29

Найкращий варіант - ВИМОЖИТИ та СТВОРИТИ обмеження зовнішніх ключів.

У цій публікації я не знайшов прикладів, які б працювали для мене "як є", один не працюватиме, якщо зовнішні ключі посилаються на різні схеми, а інші не працюватимуть, якщо іноземний ключ посилається на кілька стовпців. Цей скрипт враховує обидві схеми та кілька стовпців на зовнішній ключ.

Ось скрипт, який генерує оператори "ADD CONSTRAINT", для кількох стовпців він відокремлює їх комою ( обов'язково збережіть цей вихід перед виконанням операторів 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

Ось сценарій, який генерує висловлювання "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

5
Чи можете ви пояснити, чому це краще, ніж відключення та повторне включення обмежень?
Mahmoodvcs

Гарний сценарій. Для подібного, але альтернативного підходу дивіться: mssqltips.com/sqlservertip/3347/…
Метт Браун

11

Стандарт SQL-92 дозволяє конвеєру оголосити DEFERRABLE таким чином, щоб його можна було відкласти (неявно або явно) у межах транзакції. На жаль, SQL Server все ще відсутній у цій функції SQL-92.

Для мене зміна обмеження на NOCHECK схожа на зміну структури бази даних на ходу - скасування обмежень, безумовно, є - і чогось уникати (наприклад, користувачі вимагають збільшення привілеїв).


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

Я згоден з тобою, Гамлін. Коли ви передаєте дані за допомогою SSIS або коли хочете копіювати дані, видається цілком необхідним тимчасове вимкнення або скасування обмежень зовнішніх ключів, а потім повторне ввімкнення або відновлення їх. У цих випадках референтна цілісність не є проблемою, оскільки вона вже підтримується у вихідній базі даних. Тому ви можете бути впевнені у цьому питанні.


Цей сценарій чудово підходить для генерації моїх команд "ALTER", але як я можу їх змусити виконувати / запускати в ІП?
BlueChippy

1
Я думаю, це не спрацює, якщо будь-який із закордонних ключів є багатоколонним
Зар Шардан

Це також не створило всіх символів для дуже довгих імен таблиці / ключів.
Джошуа Дрейк

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

10

WITH CHECK CHECK майже напевно потрібно!

Цей пункт піднімався в деяких відповідях та коментарях, але я вважаю, що це досить важливо, щоб викликати це ще раз.

Повторне включення обмеження за допомогою наступної команди (ні WITH CHECK) матиме серйозні недоліки .

ALTER TABLE MyTable CHECK CONSTRAINT MyConstraint;

З ПЕРЕВІРОЮ | З НОЧЕК

Вказує, чи дані у таблиці підтверджені, чи не підтверджені відносно щойно доданого або повторно увімкненого обмеження FOREIGN KEY або CHECK. Якщо не вказано, для нових обмежень передбачається "ЗВЕРНЕННЯ", а "ЗНОЧЕК" передбачається для повторно включених обмежень.

Якщо ви не хочете перевіряти нові обмеження CHECK або FOREIGN KEY щодо існуючих даних, використовуйте NOCHECK. Ми не рекомендуємо робити це, за винятком рідкісних випадків. Нове обмеження буде оцінено в усіх наступних оновленнях даних. Будь-які порушення обмежень, які придушуються функцією NOCHECK, коли обмеження додано, можуть призвести до виходу з ладу майбутніх оновлень, якщо вони оновлюють рядки даними, які не відповідають обмеженню.

Оптимізатор запитів не враховує обмежень, визначених NOCHECK. Такі обмеження ігноруються до тих пір, поки їх не буде включено повторно за допомогою таблиці ALTER TABLE AND CHECK CHECK CONSTRAINT ALL.

Примітка: З NOCHECK за замовчуванням для повторного включення обмежень. Я маю задуматися, чому ...

  1. Жодні існуючі дані в таблиці не будуть оцінені під час виконання цієї команди - успішне завершення не є гарантією того, що дані таблиці є дійсними відповідно до обмеження.
  2. Під час наступного оновлення недійсних записів обмеження буде оцінено і не вдасться, що призведе до помилок, які можуть бути пов'язані з фактичним оновленням, яке робиться.
  3. Логіка програми, яка спирається на обмеження, щоб переконатися, що дані є дійсними, може бути невдалою.
  4. Оптимізатор запитів не використовуватиме жодних обмежень, включених таким чином.

Sys.foreign_keys вид системи забезпечує деяку видимість в це питання. Зверніть увагу, що він має is_disabledі is_not_trustedколонку. is_disabledвказує, чи будуть затверджені майбутні операції по маніпулюванню даних щодо обмеження. is_not_trustedвказує, чи всі дані, що перебувають у таблиці, підтверджені щодо обмеження.

ALTER TABLE MyTable WITH CHECK CHECK CONSTRAINT MyConstraint;

Чи можна довіряти вашим обмеженням? Дізнайтеся ...

SELECT * FROM sys.foreign_keys WHERE is_not_trusted = 1;

9

Перший пост :)

Для ОП рішення Крістофа спрацює, якщо не виникнуть проблеми з масовими проблемами з інформацією про повітряні кулі даних та журналів транзакцій. Крім того, навіть із збереженням в tlog-сховищі, оскільки видалення записує в tlog, операція може зайняти ДУЖЕ довго для таблиць із сотнями мільйонів рядків.

Я використовую серію курсорів, щоб часто усікати та перезавантажувати великі копії однієї з наших величезних виробничих баз даних. Рішення розроблено для декількох схем, декількох стовпців із зовнішнім ключем, і найкраще, що може бути відкрито для використання в SSIS.

Він передбачає створення трьох таблиць постановки (справжніх таблиць) для розміщення сценаріїв DROP, CREATE та CHECK FK, створення та вставлення цих скриптів у таблиці, а потім перекидання таблиць та їх виконання. Доданий скрипт складається з чотирьох частин: 1.) створення та зберігання скриптів у трьох поетапних (реальних) таблицях; 2.) виконання скриптів падіння FK через курсор по черзі; 3.) використання sp_MSforeachtable для усічення всіх таблиці в базі даних, окрім наших трьох таблиць поетапної роботи, і 4.) виконання створеного FK та перевірка скриптів FK в кінці вашого пакету ETL SSIS.

Запустіть частину створення сценарію в завданні Execute SQL в SSIS. Запустіть частину "Execute Drop FK Scripts" у другій роботі Execute SQL. Помістіть скрипт обрізання в третє завдання Execute SQL, а потім виконайте всі інші процеси ETL, які вам потрібно зробити, перш ніж приєднати сценарії CREATE і CHECK до остаточного завдання Execute SQL (або двох, якщо потрібно) в кінці вашого потоку управління.

Зберігання скриптів у реальних таблицях виявилося безцінним, коли повторне застосування зовнішніх ключів не вдалося, оскільки ви можете вибрати * з sync_CreateFK, скопіювати / вставити у вікно запиту, запустити їх по черзі та виправити проблеми з даними, як тільки ви знайти ті, які не вдалося / досі не вдається повторно застосувати.

Не запускайте повторно сценарій, якщо він не вдався, не впевнившись, що ви повторно застосуєте всі закордонні ключі / чеки перед цим, або, швидше за все, ви втратите деяке створення і перевірите сценарій fk, оскільки наші таблиці постановок скинуті і відтворено до створення сценаріїв для виконання.

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

7

Знайдіть обмеження

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

Виконати SQL, згенерований цим 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')

Безпечний шлях.

Примітка. Додано рішення для скасування обмеження, щоб таблицю можна було скинути або змінити без будь-якої помилки обмеження.


3

Клацніть правою кнопкою миші дизайн таблиці та перейдіть до «Відносини» та виберіть зовнішній ключ на лівій панелі та на правій панелі, встановіть для Примушення обмеження для зовнішнього ключа значення «Так» (щоб увімкнути обмеження іноземних ключів) або «Ні» (щоб відключити його). введіть тут опис зображення


3

Відповідь із позначкою "905" виглядає добре, але не працює.

Слідом працював для мене. Будь-який первинний ключ, унікальний ключ або обмеження за замовчуванням НЕ можна відключити. Насправді, якщо 'sp_helpconstraint' 'показує' n / a 'у статусі_enabled - Значить, його НЕ можна вмикати / вимикати.

- Для генерації сценарію до DISABLE

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

- Для генерації сценарію до ENABLE

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

3

Ви насправді повинні мати змогу відключити обмеження зовнішнього ключа так само, як тимчасово вимкнути інші обмеження:

Alter table MyTable nocheck constraint FK_ForeignKeyConstraintName

Просто переконайтесь, що ви відключили обмеження в першій таблиці, зазначеній у імені обмеження. Наприклад, якщо моїм обмеженням зовнішнього ключа був FK_LocationsE EmployeesLocationIdEposleeeId, я б хотів використати наступне:

Alter table Locations nocheck constraint FK_LocationsEmployeesLocationIdEmployeeId

навіть якщо порушення цього обмеження призведе до помилки, яка не обов'язково визначає цю таблицю як джерело конфлікту.


1

Один сценарій, щоб керувати ними всіма: це поєднує команди скорочення та видалення з sp_MSforeachtable, щоб уникнути скидання та відтворення обмежень - просто вкажіть таблиці, які потрібно видалити, а не усікати, і для моїх цілей я включив додатковий фільтр схеми назавжди міра (перевірена у 2008р. 2)

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

1

Ви можете тимчасово відключити обмеження на своїх таблицях, виконати роботу, а потім відновити їх.

Ось простий спосіб зробити це ...

Вимкніть усі індекси, включаючи первинні ключі, які вимкнуть усі сторонні ключі, а потім знову ввімкніть лише первинні ключі, щоб ви могли працювати з ними ...

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

[Зробіть щось, наприклад завантаження даних]

Потім знову ввімкніть та відновіть індекси ...

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

Це здавалося перспективним, але @sqlзавжди стає усіченим. :(
Буде

1

У мене є більш корисна версія, якщо вас цікавить. Я підняв трохи коду звідси веб-сайту, на якому посилання більше не активне. Я модифікував це, щоб дозволити масив таблиць у збереженій процедурі, і він заповнює крапку, усікає, додає оператори перед виконанням усіх. Це дає вам можливість вирішувати, які таблиці потребують обрізання.

/****** 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

Мертве посилання у вашій відповіді. Вказує на пусту статтю.
Неоліск

Привіт, цілком може бути мертве посилання, але весь код вказаний у творі. Що з цим не так?
Зак Вілліс

Нічого поганого, але ви, ймовірно, повинні відредагувати свою відповідь та видалити мертве посилання.
Неоліск
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.