Ответ 1
Для SQL 2005
EXEC sp_MSForEachTable 'TRUNCATE TABLE ?'
У меня есть тестовая среда для базы данных, которую я хочу перезагрузить с новыми данными в начале цикла тестирования. Меня не интересует перестройка всей базы данных - просто "переустановка" данных.
Каков наилучший способ удалить все данные из всех таблиц с помощью TSQL? Существуют ли системные хранимые процедуры, представления и т.д., Которые можно использовать? Я не хочу вручную создавать и поддерживать инструкции таблицы усечений для каждой таблицы - я бы предпочел, чтобы она была динамической.
Для SQL 2005
EXEC sp_MSForEachTable 'TRUNCATE TABLE ?'
Когда вы занимаетесь удалением данных из таблиц, которые имеют отношения с внешним ключом, что в основном относится к любой правильно разработанной базе данных, мы можем отключить все ограничения, удалить все данные и затем снова включить ограничения
-- disable all constraints
EXEC sp_MSForEachTable "ALTER TABLE ? NOCHECK CONSTRAINT all"
-- delete data in all tables
EXEC sp_MSForEachTable "DELETE FROM ?"
-- enable all constraints
exec sp_MSForEachTable "ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"
Подробнее о отключении ограничений и триггеров здесь
если в некоторых таблицах есть столбцы с идентификаторами, мы можем захотеть их перезагрузить.
EXEC sp_MSForEachTable "DBCC CHECKIDENT ( '?', RESEED, 0)"
Обратите внимание, что поведение RESEED отличается от новой таблицы, а другое, которое было ранее добавлено из BOL:
DBCC CHECKIDENT ('table_name', RESEED, newReseedValue)
Текущее значение идентификатора установлено равным newReseedValue. Если никакие строки не имеют был вставлен в таблицу, так как, первая строка вставлена после выполнения DBCC CHECKIDENT используйте newReseedValue как идентификатор. В противном случае добавленная следующая строка будет используйте newReseedValue + 1. Если значение newReseedValue меньше, чем максимальное значение в столбце идентификации, сообщение об ошибке 2627 будет сгенерировано на последующих ссылках на таблицу.
Благодаря Robert за то, что отключить ограничения не позволяет использовать truncate, ограничения должны быть отброшены, а затем воссозданы
Вот король-папа для очистки баз данных. Он очистит все таблицы и вернет их правильно:
SET QUOTED_IDENTIFIER ON;
EXEC sp_MSforeachtable 'SET QUOTED_IDENTIFIER ON; ALTER TABLE ? NOCHECK CONSTRAINT ALL'
EXEC sp_MSforeachtable 'SET QUOTED_IDENTIFIER ON; ALTER TABLE ? DISABLE TRIGGER ALL'
EXEC sp_MSforeachtable 'SET QUOTED_IDENTIFIER ON; DELETE FROM ?'
EXEC sp_MSforeachtable 'SET QUOTED_IDENTIFIER ON; ALTER TABLE ? CHECK CONSTRAINT ALL'
EXEC sp_MSforeachtable 'SET QUOTED_IDENTIFIER ON; ALTER TABLE ? ENABLE TRIGGER ALL'
EXEC sp_MSforeachtable 'SET QUOTED_IDENTIFIER ON;
IF NOT EXISTS (
SELECT
*
FROM
SYS.IDENTITY_COLUMNS
JOIN SYS.TABLES ON SYS.IDENTITY_COLUMNS.Object_ID = SYS.TABLES.Object_ID
WHERE
SYS.TABLES.Object_ID = OBJECT_ID(''?'') AND SYS.IDENTITY_COLUMNS.Last_Value IS NULL
)
AND OBJECTPROPERTY( OBJECT_ID(''?''), ''TableHasIdentity'' ) = 1
DBCC CHECKIDENT (''?'', RESEED, 0) WITH NO_INFOMSGS'
Наслаждайтесь, но будьте осторожны!
Самый простой способ сделать это -
это даст вам script, который уменьшает и воссоздает все ваши таблицы без необходимости беспокоиться об отладке или включил ли вы все. Хотя это выполняет больше, чем просто усечение, результаты одинаковы. Просто имейте в виду, что ваши автоматически увеличивающиеся первичные ключи начинаются с 0, в отличие от усеченных таблиц, которые будут помнить последнее назначенное значение. Вы также можете выполнить это из кода, если у вас нет доступа к студию управления в средах PreProd или Production.
1.
2.
3.
Усечение всех таблиц будет работать только в том случае, если у вас нет внешних связей между вашими таблицами, поскольку SQL Server не позволит вам обрезать таблицу с помощью внешнего ключа.
Альтернативой этому является определение таблиц с внешними ключами и удаление из них в первую очередь, после этого вы можете обрезать таблицы без внешних ключей.
Смотрите http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=65341 и http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=72957 для дальнейшие подробности.
Альтернативный вариант, который мне нравится использовать с MSSQL Server Deveploper или Enterprise, заключается в создании моментального снимка базы данных сразу после создания пустой схемы. В этот момент вы можете просто восстановить базу данных обратно к снимку.
Не делай этого! На самом деле, это не очень хорошая идея.
Если вы знаете, какие таблицы вы хотите усечь, создайте хранимую процедуру, которая усекает их. Вы можете исправить этот заказ, чтобы избежать проблем с внешним ключом.
Если вы действительно хотите усечь их все (чтобы вы могли загружать их BCP, например), вы бы так же быстро сбросили базу данных и создали новую с нуля, что принесло бы дополнительную выгоду, которую вы точно знаете вы.
Создайте пустую базу данных "шаблон", возьмите полную резервную копию. Когда вам нужно обновиться, просто восстановите его, используя WITH REPLACE. Быстрая, простая, пуленепробиваемая. И если пара таблиц здесь или там нуждается в некоторых базовых данных (например, информация о конфигурации или просто базовая информация, которая заставляет ваше приложение запускаться), это также обрабатывает.
Если вы хотите хранить данные в определенной таблице (т.е. статическую таблицу поиска) при удалении/усечении данных в других таблицах в одном и том же двоичном формате, вам нужен цикл с исключениями в нем. Это то, что я искал, когда я наткнулся на этот вопрос.
sp_MSForEachTable кажется мне ошибкой (т.е. непоследовательное поведение с операторами IF), что, вероятно, почему его недокументировано MS.
declare @LastObjectID int = 0
declare @TableName nvarchar(100) = ''
set @LastObjectID = (select top 1 [object_id] from sys.tables where [object_id] > @LastObjectID order by [object_id])
while(@LastObjectID is not null)
begin
set @TableName = (select top 1 [name] from sys.tables where [object_id] = @LastObjectID)
if(@TableName not in ('Profiles', 'ClientDetails', 'Addresses', 'AgentDetails', 'ChainCodes', 'VendorDetails'))
begin
exec('truncate table [' + @TableName + ']')
end
set @LastObjectID = (select top 1 [object_id] from sys.tables where [object_id] > @LastObjectID order by [object_id])
end
Это один из способов сделать это... есть, вероятно, 10 других, которые лучше/эффективнее, но похоже, что это делается очень редко, поэтому здесь идет...
получить список таблиц из sysobjects, затем перебрать их с помощью курсора, вызывая sp_execsql ('truncate table' + @table_name) для каждой итерации.
Это намного проще (и, возможно, даже быстрее) до script из вашей базы данных, а затем просто снимите и создайте его из script.
Самая сложная часть обрезания всех таблиц - удаление и повторное добавление ограничений внешнего ключа.
Следующий запрос создает инструкции drop и create для каждого ограничения, относящиеся к каждому имени таблицы в @myTempTable. Если вы хотите сгенерировать их для всех таблиц, вы можете просто использовать информационную схему для сбора этих имен таблиц.
DECLARE @myTempTable TABLE (tableName varchar(200))
INSERT INTO @myTempTable(tableName) VALUES
('TABLE_ONE'),
('TABLE_TWO'),
('TABLE_THREE')
-- DROP FK Contraints
SELECT 'alter table '+quotename(schema_name(ob.schema_id))+
'.'+quotename(object_name(ob.object_id))+ ' drop constraint ' + quotename(fk.name)
FROM sys.objects ob INNER JOIN sys.foreign_keys fk ON fk.parent_object_id = ob.object_id
WHERE fk.referenced_object_id IN
(
SELECT so.object_id
FROM sys.objects so JOIN sys.schemas sc
ON so.schema_id = sc.schema_id
WHERE so.name IN (SELECT * FROM @myTempTable) AND sc.name=N'dbo' AND type in (N'U'))
-- CREATE FK Contraints
SELECT 'ALTER TABLE [PIMSUser].[dbo].[' +cast(c.name as varchar(255)) + '] WITH NOCHECK ADD CONSTRAINT ['+ cast(f.name as varchar(255)) +'] FOREIGN KEY (['+ cast(fc.name as varchar(255)) +'])
REFERENCES [PIMSUser].[dbo].['+ cast(p.name as varchar(255)) +'] (['+cast(rc.name as varchar(255))+'])'
FROM sysobjects f
INNER JOIN sys.sysobjects c ON f.parent_obj = c.id
INNER JOIN sys.sysreferences r ON f.id = r.constid
INNER JOIN sys.sysobjects p ON r.rkeyid = p.id
INNER JOIN sys.syscolumns rc ON r.rkeyid = rc.id and r.rkey1 = rc.colid
INNER JOIN sys.syscolumns fc ON r.fkeyid = fc.id and r.fkey1 = fc.colid
WHERE
f.type = 'F'
AND
cast(p.name as varchar(255)) IN (SELECT * FROM @myTempTable)
Затем я просто скопирую инструкции для запуска - но с небольшим усилием dev вы можете использовать курсор для динамического их запуска.
Я не понимаю, почему очистка данных будет лучше, чем script, чтобы удалить и заново создать каждую таблицу.
Это или сохранить резервную копию вашей пустой БД и восстановить ее поверх старой
Перед усечением таблиц вам нужно удалить все внешние ключи. Используйте script, чтобы генерировать окончательные сценарии для удаления и воссоздания всех внешних ключей в базе данных. Установите переменную @action в "CREATE" или "DROP".
Запустите раздел с комментариями один раз, заполните таблицу _TruncateList таблицами, которые вы хотите усечь, затем запустите оставшуюся часть script. Таблицу _ScriptLog нужно будет очистить со временем, если вы сделаете это много.
Вы можете изменить это, если хотите сделать все таблицы, просто введите имя SELECT INTO #TruncateList FROM sys.tables. Однако вы обычно не хотите делать все это.
Кроме того, это повлияет на все внешние ключи в базе данных, и вы также можете изменить это, если это слишком тупые силы для вашего приложения. Это не для моих целей.
/*
CREATE TABLE _ScriptLog
(
ID Int NOT NULL Identity(1,1)
, DateAdded DateTime2 NOT NULL DEFAULT GetDate()
, Script NVarChar(4000) NOT NULL
)
CREATE UNIQUE CLUSTERED INDEX IX_ScriptLog_DateAdded_ID_U_C ON _ScriptLog
(
DateAdded
, ID
)
CREATE TABLE _TruncateList
(
TableName SysName PRIMARY KEY
)
*/
IF OBJECT_ID('TempDB..#DropFK') IS NOT NULL BEGIN
DROP TABLE #DropFK
END
IF OBJECT_ID('TempDB..#TruncateList') IS NOT NULL BEGIN
DROP TABLE #TruncateList
END
IF OBJECT_ID('TempDB..#CreateFK') IS NOT NULL BEGIN
DROP TABLE #CreateFK
END
SELECT Scripts = 'ALTER TABLE ' + '[' + OBJECT_NAME(f.parent_object_id)+ ']'+
' DROP CONSTRAINT ' + '[' + f.name + ']'
INTO #DropFK
FROM .sys.foreign_keys AS f
INNER JOIN .sys.foreign_key_columns AS fc
ON f.OBJECT_ID = fc.constraint_object_id
SELECT TableName
INTO #TruncateList
FROM _TruncateList
SELECT Scripts = 'ALTER TABLE ' + const.parent_obj + '
ADD CONSTRAINT ' + const.const_name + ' FOREIGN KEY (
' + const.parent_col_csv + '
) REFERENCES ' + const.ref_obj + '(' + const.ref_col_csv + ')
'
INTO #CreateFK
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
INSERT INTO _ScriptLog (Script)
SELECT Scripts
FROM #CreateFK
DECLARE @Cmd NVarChar(4000)
, @TableName SysName
WHILE 0 < (SELECT Count(1) FROM #DropFK) BEGIN
SELECT TOP 1 @Cmd = Scripts
FROM #DropFK
EXEC (@Cmd)
DELETE #DropFK WHERE Scripts = @Cmd
END
WHILE 0 < (SELECT Count(1) FROM #TruncateList) BEGIN
SELECT TOP 1 @Cmd = N'TRUNCATE TABLE ' + TableName
, @TableName = TableName
FROM #TruncateList
EXEC (@Cmd)
DELETE #TruncateList WHERE TableName = @TableName
END
WHILE 0 < (SELECT Count(1) FROM #CreateFK) BEGIN
SELECT TOP 1 @Cmd = Scripts
FROM #CreateFK
EXEC (@Cmd)
DELETE #CreateFK WHERE Scripts = @Cmd
END
выберите 'delete from' + TABLE_NAME из INFORMATION_SCHEMA.TABLES, где TABLE_TYPE = 'BASE TABLE'
где результат.
Скопируйте и вставьте в окно запроса и запустите команду