Отключение ограничения внешнего ключа, по-прежнему не удается усечь таблицу? (SQL Server 2005)

У меня есть таблица под названием PX_Child, у которой есть внешний ключ на PX_Parent. Я хотел бы временно отключить это ограничение FK, чтобы можно было обрезать PX_Parent. Однако я не уверен, как это происходит.

Я пробовал эти команды

ALTER TABLE PX_Child NOCHECK CONSTRAINT ALL

ALTER TABLE PX_Parent NOCHECK CONSTRAINT ALL

(truncate commands)

ALTER TABLE PX_Child CHECK CONSTRAINT ALL

ALTER TABLE PX_Parent CHECK CONSTRAINT ALL

Но усечение по-прежнему говорит мне, что не может усечь PX_Parent из-за ограничения внешнего ключа. Я просмотрел всю сеть и, похоже, не нашел, что делаю неправильно, извините за основной характер этого вопроса.


person larryq    schedule 02.10.2010    source источник
comment
Похоже, что Кален Делейни был непреднамеренно ответственен за начало этой идеи. Здесь она поясняет, что вы должны отказаться от ограничения ссылки чтобы усечь таблицу.   -  person Martin Smith    schedule 02.10.2010


Ответы (4)


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

person bobs    schedule 02.10.2010
comment
См. Мой ответ (5 лет спустя) ниже, чтобы узнать, как быстро сгенерировать DROP CONSTRAINT и ADD CONSTRAINT SQL. - person RJB; 24.04.2016
comment
Оператор DELETE конфликтует с ограничением REFERENCE. (Удалить тоже не работает) - person Enrico; 17.12.2019

Есть способ попроще. Я столкнулся с той же проблемой и нашел следующее решение: https://www.mssqltips.com/sqlservertip/3347/drop-and-recreate-all-foreign-key-constraints-in-sql-server/

Если вы просто запустите этот запрос в своей БД, он сгенерирует T-SQL, который вам нужно включить до / после вашего sproc, чтобы удалить, а затем восстановить любые ограничения внешнего ключа.

Не беспокойтесь о попытках понять сам этот запрос.

CREATE TABLE #x -- feel free to use a permanent table
(
  drop_script NVARCHAR(MAX),
  create_script NVARCHAR(MAX)
);

DECLARE @drop   NVARCHAR(MAX) = N'',
        @create NVARCHAR(MAX) = N'';

-- drop is easy, just build a simple concatenated list from sys.foreign_keys:
SELECT @drop += N'
ALTER TABLE ' + QUOTENAME(cs.name) + '.' + QUOTENAME(ct.name) 
    + ' DROP CONSTRAINT ' + QUOTENAME(fk.name) + ';'
FROM sys.foreign_keys AS fk
INNER JOIN sys.tables AS ct
  ON fk.parent_object_id = ct.[object_id]
INNER JOIN sys.schemas AS cs 
  ON ct.[schema_id] = cs.[schema_id];

INSERT #x(drop_script) SELECT @drop;

-- create is a little more complex. We need to generate the list of 
-- columns on both sides of the constraint, even though in most cases
-- there is only one column.
SELECT @create += N'
ALTER TABLE ' 
   + QUOTENAME(cs.name) + '.' + QUOTENAME(ct.name) 
   + ' ADD CONSTRAINT ' + QUOTENAME(fk.name) 
   + ' FOREIGN KEY (' + STUFF((SELECT ',' + QUOTENAME(c.name)
   -- get all the columns in the constraint table
    FROM sys.columns AS c 
    INNER JOIN sys.foreign_key_columns AS fkc 
    ON fkc.parent_column_id = c.column_id
    AND fkc.parent_object_id = c.[object_id]
    WHERE fkc.constraint_object_id = fk.[object_id]
    ORDER BY fkc.constraint_column_id 
    FOR XML PATH(N''), TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 1, N'')
  + ') REFERENCES ' + QUOTENAME(rs.name) + '.' + QUOTENAME(rt.name)
  + '(' + STUFF((SELECT ',' + QUOTENAME(c.name)
   -- get all the referenced columns
    FROM sys.columns AS c 
    INNER JOIN sys.foreign_key_columns AS fkc 
    ON fkc.referenced_column_id = c.column_id
    AND fkc.referenced_object_id = c.[object_id]
    WHERE fkc.constraint_object_id = fk.[object_id]
    ORDER BY fkc.constraint_column_id 
    FOR XML PATH(N''), TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 1, N'') + ');'
FROM sys.foreign_keys AS fk
INNER JOIN sys.tables AS rt -- referenced table
  ON fk.referenced_object_id = rt.[object_id]
INNER JOIN sys.schemas AS rs 
  ON rt.[schema_id] = rs.[schema_id]
INNER JOIN sys.tables AS ct -- constraint table
  ON fk.parent_object_id = ct.[object_id]
INNER JOIN sys.schemas AS cs 
  ON ct.[schema_id] = cs.[schema_id]
WHERE rt.is_ms_shipped = 0 AND ct.is_ms_shipped = 0;

UPDATE #x SET create_script = @create;

PRINT @drop;
PRINT @create;

/*
EXEC sp_executesql @drop
-- clear out data etc. here
EXEC sp_executesql @create;
*/

Создает кучу:

ALTER TABLE [dbo].[Whatever] DROP CONSTRAINT....
--
ALTER TABLE [dbo].[Whatever] ADD CONSTRAINT....
person RJB    schedule 24.04.2016
comment
плохой совет: не беспокойтесь о попытках понять сам этот запрос. Никогда не запускайте что-либо, полученное из сети, не понимая этого - person Dharmendar Kumar 'DK'; 22.05.2016
comment
Верно, но это не производственная версия БД. - person RJB; 23.05.2016
comment
Есть разница между пониманием того, как работает запрос, и уверенностью, что он не причинит никакого вреда. Последнее почти всегда проще. - person wolfrevokcats; 09.07.2016
comment
Большое спасибо за этот сценарий! Потребовалось время, чтобы понять себя. Хотя я это понимаю, я полностью согласен, вам не обязательно понимать это, чтобы использовать. Просто убедитесь, что код, который он генерирует, верен, прежде чем запускать его. - person Brandon Frenchak; 21.03.2017
comment
Тот факт, что этот беспорядок динамического sql необходим для простого выполнения TRUNCATE TABLE, отвратителен. - person Pxtl; 18.05.2017
comment
Как написано, это не воссоздает должным образом ограничения с правилами ON DELETE CASCADE. Это простое исправление, измените строку FOR XML PATH(N''), TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 1, N'') + ');' на FOR XML PATH(N''), TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 1, N'') + ') ON DELETE ' + REPLACE(fk.delete_referential_action_desc, '_', ' ') + ';' - person Jason Larke; 14.09.2017
comment
Обратите внимание, что операторы PRINT @drop и PRINT @create будут усекаться в SQL Management Studio. Если вам нужны эти полные значения, вам нужно SELECT * FROM #x, а затем скопировать значения из двух результирующих столбцов. Даже тогда мне пришлось DECLARE @tempCreate NVARCHAR(MAX); SELECT @tempCreate = create_script from #x; EXEC sp_executesql @tempCreate; для моего оператора create. - person Tyler Forsythe; 16.03.2018
comment
Этот сценарий не работал с моими таблицами, должно быть, он что-то упустил. - person jnnnnn; 05.09.2018
comment
Как сказал Тайлер, он обрежет текст, если он будет слишком длинным. Только что попробовал в SQL Ops Studio (теперь известной как Azure Data Studio) с таким же усечением. Простое добавление select @drop union all select @create в конце скрипта решит проблему. - person Adam Plocher; 24.10.2018
comment
Если вы получаете сообщение об ошибке Не удается разрешить конфликт сопоставления ... после внесения изменения, предложенного @JasonLarke, вы можете добавить сопоставление database_default в конец запроса. См. github.com/Microsoft/mssql-jdbc/issues/590. - person ajphall; 21.10.2019

SQL-сервер не позволит вам обрезать таблицу, пока существует ограничение, даже если оно отключено. Отбросьте ограничение и заново создайте его после усечения таблицы. Или просто отбросьте и заново создайте таблицы, в зависимости от того, что проще сделать в вашем приложении.

person Paul Groke    schedule 02.10.2010
comment
Что вы имеете в виду, что это не транзакционная команда? Вы можете просто откатить его обратно. CREATE TABLE Blah(a int); INSERT Blah VALUES(1); SELECT * FROM Blah; BEGIN TRAN; TRUNCATE TABLE Blah; SELECT * FROM Blah; ROLLBACK TRAN SELECT * FROM Blah; DROP TABLE Blah. Truncate работает, освобождая целые страницы, а не удаляя строки, но он по-прежнему транзакционный. - person ErikE; 02.10.2010
comment
@Emtucifor: Ой, похоже, я неправильно истолковал документацию, ты прав! Я удалил эту дезинформацию. - person Paul Groke; 02.10.2010
comment
@Emtucifor, @pgroke, в каком-то смысле вы оба правы, поскольку стандарт позволяет TRUNCATE быть нетранзакционным, но реализациям разрешено делать его транзакционным. Следовательно, TRUNCATE, как определено, не обещает, что откат может быть выполнен, но SqlServer (и Postgres) добавляет это обещание сверх стандарта. - person Jon Hanna; 02.10.2010
comment
@Jon Спасибо за разъяснения. Позвольте перефразировать. В SQL Server усечение является транзакционным. - person ErikE; 03.10.2010

Нет такой возможности обрезать таблицу при ограничении внешнего ключа, но мы можем использовать некоторый трюк, например

 ALTER TABLE [dbo].[table2] DROP CONSTRAINT [FK_table2_table1]
    GO
    truncate table [table1]
GO
    ALTER TABLE [dbo].[table2]  WITH CHECK ADD  CONSTRAINT [FK_table2_table1] FOREIGN KEY([FKId])
    REFERENCES [dbo].[table1] ([ID])
    GO

    ALTER TABLE [dbo].[table2] CHECK CONSTRAINT [FK_table2_table1]
    GO
person Bhargil Joshi    schedule 15.04.2019