Как можно временно отключить ограничения внешнего ключа с помощью T-SQL?

824

Поддерживается ли отключение и включение ограничений внешнего ключа в SQL Server? Или это единственный вариант для меня, dropа затем повторноcreate ограничения?

луч
источник
128
Для людей, спрашивающих «почему», я хотел бы сделать это: это для тестовой среды, где я хочу иметь возможность удалять и загружать тестовые данные из нескольких таблиц без необходимости поддерживать и указывать порядок загрузки данных. Целостность данных не так важна для этого сценария.
Рэй
8
Примечание. Если вы хотите TRUNCATE таблицы, вам нужно фактически удалить ограничения.
Выдающийся
@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 - нет, вам нужны оба
Кристоф
32
@CrazyPyro: оба действительно необходимы, причина в том, что первый CHECK принадлежит WITH, а второй CHECK - CONSTRAINT (это тип ограничения). Первый CHECK гарантирует, что ваши данные проверяются на согласованность при активации ограничения. Если вы не хотите этого, вы можете написать WITH NOCHECK. Может быть полезно в определенных тестовых ситуациях, когда вас не интересуют фактические данные, если они есть, чтобы в ваших запросах было что поиграть.
Валентино Вранкен
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
ScottStonehouse
источник
29
хорошая находка, но учтите, что вы все еще не можете обрезать таблицу, не удаляя ограничения внешнего ключа
Стивен А. Лоу
1
и вам также нужно знать, что когда вы снова включите ограничения и выполните проверку целостности данных, ваши данные могут потерпеть неудачу, и такая проблема может стать кошмаром, если ошибочные данные находятся в конце длинной строки связанные ограничения.
Джимок
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]
  • Обратите внимание на двойную проверку при включении.
  • ALL означает все ограничения в таблице.

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

    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 
Диего Мендес
источник
Не просматривать для первичных ключей ? Для внешних ключей SYSFOREIGNKEYS System View 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
Vic
источник
5
Можете ли вы объяснить, почему это лучше, чем отключение и повторное включение ограничений?
Mahmoodvcs
Хороший сценарий. Аналогичный, но альтернативный подход см. По адресу
Мэтт Браун,
11

Стандарт SQL-92 позволяет объявлять консистент как DEFERRABLE, чтобы его можно было откладывать (неявно или явно) в рамках транзакции. К сожалению, SQL Server все еще не хватает этой функциональности SQL-92.

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

onedaywhen
источник
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», но как я могу заставить их выполнять / запускать в SP?
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 почти наверняка требуется!

Этот вопрос был поднят в некоторых ответах и ​​комментариях, но я чувствую, что это достаточно важно, чтобы повторить это.

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

ALTER TABLE MyTable CHECK CONSTRAINT MyConstraint;

С проверкой | С NOCHECK

Указывает, проверяются ли данные в таблице на соответствие вновь добавленным или повторно включенным ограничениям FOREIGN KEY или CHECK. Если не указано, WITH CHECK предполагается для новых ограничений, а WITH NOCHECK предполагается для повторно включенных ограничений.

Если вы не хотите проверять новые ограничения CHECK или FOREIGN KEY для существующих данных, используйте WITH NOCHECK. Мы не рекомендуем делать это, за исключением редких случаев. Новое ограничение будет оцениваться во всех последующих обновлениях данных. Любые нарушения ограничений, которые подавляются WITH NOCHECK при добавлении ограничения, могут привести к сбою будущих обновлений, если они обновляют строки данными, которые не соответствуют ограничению.

Оптимизатор запросов не учитывает ограничения, которые определены с помощью NOCHECK. Такие ограничения игнорируются, пока они не будут повторно включены с помощью таблицы ALTER TABLE WITH CHECK CHECK CONSTRAINT ALL.

Примечание: WITH 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

Первый пост :)

Для OP решение Кристофа будет работать, если только не возникнут проблемы с большими объемами данных и всплывающими подсказками журнала транзакций при больших удалениях. Кроме того, даже при сохранении хранилища tlog, поскольку удаляет запись в tlog, операция может занять ОЧЕНЬ много времени для таблиц с сотнями миллионов строк.

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

Он включает в себя создание трех промежуточных таблиц (реальных таблиц) для размещения сценариев DROP, CREATE и CHECK FK, создание и вставку этих сценариев в таблицы, а затем циклическое перемещение по таблицам и их выполнение. Прикрепленный сценарий состоит из четырех частей: 1.) создание и хранение сценариев в трех промежуточных (реальных) таблицах, 2.) выполнение сценариев удаления FK с помощью курсора одна за другой, 3.) использование sp_MSforeachtable для усечения всех таблиц в базе данных, отличных от наших трех промежуточных таблиц и 4.) выполнения команд create FK и проверки сценариев FK в конце пакета ETL SSIS.

Запустите часть создания сценария в задаче «Выполнение SQL» в службах SSIS. Запустите часть «Выполнить удаление сценариев FK» во второй задаче «Выполнение SQL». Поместите сценарий усечения в третью задачу «Выполнение SQL», затем выполните все другие процессы ETL, которые вам нужно выполнить, прежде чем присоединять сценарии CREATE и CHECK к окончательной задаче «Выполнение 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
Denmach
источник
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')

Safeway.

Примечание. Добавлено решение для удаления ограничения, чтобы таблица могла быть удалена или изменена без каких-либо ошибок ограничения.

Адитья
источник
3

Щелкните правой кнопкой мыши дизайн таблицы и перейдите к Отношениям и выберите внешний ключ на левой панели и на правой панели, установите для параметра Принудительное ограничение внешнего ключа значение «Да» (чтобы включить ограничения внешнего ключа) или «Нет» (чтобы отключи это). введите описание изображения здесь

Амир Хоссейн Маниан
источник
3

Ответ с пометкой «905» выглядит хорошо, но не работает.

Следующее сработало для меня. Любые ограничения первичного ключа, уникального ключа или значения по умолчанию НЕ МОГУТ быть отключены. Фактически, если 'sp_helpconstraint' 'показывает' n / a 'в status_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_LocationsEmployeesLocationIdEmployeeId, я хотел бы использовать следующее:

Alter table Locations nocheck constraint FK_LocationsEmployeesLocationIdEmployeeId

даже если нарушение этого ограничения приведет к ошибке, которая не обязательно будет указывать эту таблицу как источник конфликта.

lwilliams
источник
1

Один сценарий, чтобы управлять ими всеми: он объединяет команды усечения и удаления с sp_MSforeachtable, так что вы можете избежать отбрасывания и воссоздания ограничений - просто укажите таблицы, которые нужно удалять, а не усекать, и для моих целей я добавил дополнительный фильтр схемы для хорошего мера (проверено в 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
Алекс Хинтон
источник
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

У меня есть более полезная версия, если вам интересно. Я поднял немного кода с сайта, на котором ссылка больше не активна. Я изменил его, чтобы разрешить массив таблиц в хранимой процедуре, и он заполняет операторы drop, truncate, add перед выполнением всех из них. Это дает вам возможность определять, какие таблицы нужно обрезать.

/****** 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
Зак Уиллис
источник
Мертвая ссылка в вашем ответе. Указывает на пустую статью.
Неолиск
Привет, вполне может быть мертвая ссылка, но весь код указан в куске. Что не так с этим?
Зак Уиллис
Ничего плохого, но вы, вероятно, должны отредактировать свой ответ и удалить неработающую ссылку.
Neolisk