Как вы усекаете все таблицы в базе данных, используя TSQL?

204

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

Каков наилучший способ удалить все данные из всех таблиц с помощью TSQL? Существуют ли системные хранимые процедуры, представления и т. Д., Которые можно использовать? Я не хочу вручную создавать и поддерживать усеченные операторы таблиц для каждой таблицы - я бы предпочел, чтобы они были динамическими.

луч
источник

Ответы:

188

Для SQL 2005

EXEC sp_MSForEachTable 'TRUNCATE TABLE ?'

Еще пара ссылок на 2000 и 2005/2008 годы .

Гульзар Назим
источник
62
Вы не можете усекать таблицы с внешними ключами, поэтому это будет работать только в том случае, если между таблицами нет ограничений внешнего ключа (или они были отключены).
marcj
1
согласился .. я думал, так как он специально попросил обрезать таблицы, он уже решил проблему с внешними ключами ..
Гульзар Назим
@ gulzar - вроде - я опубликовал отдельный вопрос о том, как обращаться с ФК, но ваш ответ стоит по существу.
Рэй
11
@ Сэм: Нет, не будет! Данные в таблицах не имеют значения. Пока существует ограничение внешнего ключа, ссылающееся на таблицу (даже отключенное), вы не сможете ее усечь.
TToni
3
'EXEC sp_MSForEachTable' DROP TABLE? ' Также отлично работает :) (восхищает все таблицы из базы данных)
kuncevic.dev
419

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

-- 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 ('имя_таблицы', RESEED, newReseedValue)

Текущее значение идентификатора установлено в newReseedValue. Если в таблицу с момента ее создания не было вставлено ни одной строки, первая строка, вставленная после выполнения DBCC CHECKIDENT, будет использовать newReseedValue в качестве идентификатора. В противном случае следующая вставленная строка будет использовать newReseedValue + 1. Если значение newReseedValue меньше максимального значения в столбце идентификаторов, сообщение об ошибке 2627 будет сгенерировано при последующих ссылках на таблицу.

Спасибо Роберту за то, что он указал на тот факт, что отключение ограничений не позволяет использовать усечение, ограничения должны быть удалены, а затем воссозданы

Кристофа
источник
34
Отключение ограничений НЕ позволит обрезать таблицы, на которые ссылается ограничение FOREIGN KEY. Ограничение FK должно быть снято. Пожалуйста, ответьте, если я ошибаюсь по этому поводу, но я не нашел способа избежать их отбрасывания.
Роберт Клэйпул
1
В этом выражении не должно быть просто опечатки "Table", ключевое слово EXEC sp_MSForEachTable "DELETE FROM TABLE?" . Правильная версия должна быть: EXEC sp_MSForEachTable "DELETE FROM?"
Рагхав
4
Если вы используете SSMS 2008 или более новую версию, вы, вероятно, захотите добавить ее SET ROWCOUNT 0в начало вашего сценария, поскольку по умолчанию ограничено количество действий до 500 строк! Вы получите разочаровывающие ошибки, как я сделал, потому что не все данные будут фактически удалены.
Шон Хэнли
1
Это сработало отлично. В моем случае мне также пришлось добавить EXEC sp_msforeachtable «ALTER TABLE? Отключить TRIGGER все» и EXEC sp_msforeachtable «ALTER TABLE? Включить TRIGGER все» до и после оператора delete.
RobC
2
Мой любимый ответ Но почему вы (все, даже комментаторы) заключаете литеральные строки SQL в двойные кавычки?
bitoolean
57

Вот король папа сценариев очистки базы данных. Он очистит все таблицы и правильно их заполнит:

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;

Наслаждайтесь, но будьте осторожны!

Крис К.Л.
источник
2
К сожалению, приведенная выше команда завершается неудачно, если у вас есть вычисляемые столбцы, поскольку sp_MSforeachtable, очевидно, имеет SET QUOTED_IDENTITY OFFв своем теле ( ссылка ). ОБНОВЛЕНИЕ: исправление заключается в добавлении «SET QUOTED_IDENTIFIERS on;» в начале каждого утверждения, которое вызывает эту ошибку (как упоминалось здесь )
Marchy
1
кажется, это не переопределяет мою личность
totooooo
48

Самый простой способ сделать это

  1. открыть SQL Management Studio
  2. перейти к вашей базе данных
  3. Щелкните правой кнопкой мыши и выберите «Задачи» -> «Создать сценарии» (рис. 1).
  4. На экране «Выбор объектов» выберите опцию «Выбрать конкретные объекты» и проверьте «таблицы» (рис. 2).
  5. на следующем экране выберите «расширенный», а затем измените параметр «Script DROP and CREATE» на «Script DROP and CREATE» (рис. 3)
  6. Выберите, чтобы сохранить сценарий в новом окне редактора или в файле и запускать при необходимости.

это даст вам сценарий, который удаляет и воссоздает все ваши таблицы без необходимости беспокоиться об отладке или о том, включены ли вы все. Хотя это выполняет больше, чем просто усечение, результаты одинаковы. Просто имейте в виду, что ваши автоматически увеличивающиеся первичные ключи будут начинаться с 0, в отличие от усеченных таблиц, которые будут помнить последнее назначенное значение. Вы также можете выполнить это из кода, если у вас нет доступа к Management studio в вашей среде PreProd или Production.

1.

введите описание изображения здесь

2.

введите описание изображения здесь

3.

введите описание изображения здесь

Капитан кенпачи
источник
1
Осторожно, если вы используете это для базы данных с очень сложной схемой. Я попробовал это на dev-копии нашей производственной БД, и она разрушила схему, требуя полного повторного развертывания.
Techrocket9
1
Вы должны
капитан Кенпачи
13

Усечение всех таблиц будет работать только в том случае, если между таблицами нет взаимосвязей с внешним ключом, поскольку SQL Server не позволит вам усекать таблицу с внешним ключом.

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

См. Http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=65341 и http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=72957 для получения дополнительной информации.

marcj
источник
1
Хорошая точка зрения. Не думал об этом. Я мог бы сначала отключить все ограничения, а затем снова включить их после удаления данных.
Рэй
7

Альтернативный вариант, который мне нравится использовать с MSSQL Server Deveploper или Enterprise, - это создание моментального снимка базы данных сразу после создания пустой схемы. На этом этапе вы можете просто восстановить базу данных обратно в моментальный снимок.

Крис Чилверс
источник
К сожалению, вы теряете все свои индексы FULLTEXT каждый раз
Крис KL
6

Не делай этого! На самом деле, не очень хорошая идея.

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

Если вы действительно хотите обрезать их все (например, вы можете загрузить их с помощью BCP), вы также можете быстро удалить базу данных и создать новую с нуля, что даст вам дополнительное преимущество, если вы точно будете знать, где вы находитесь.

Бен Лиддикотт
источник
Хороший альтернативный подход здесь.
Сэм
3
проблема с вашим подходом состоит в том, что удаление таблиц и базы данных приведет к потере всех разрешений, которые были предоставлены для разных имен входа и схем. Воссоздать это будет больно для больших баз данных с большим количеством таблиц.
Punit Vora
4

Если вы хотите сохранить данные в определенной таблице (то есть в статической таблице поиска) при удалении / усечении данных в других таблицах в том же БД, то вам нужен цикл с исключениями в нем. Это то, что я искал, когда наткнулся на этот вопрос.

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
Крис Смит
источник
4

Самая сложная часть усечения всех таблиц - это удаление и повторное добавление ограничений внешнего ключа.

Следующий запрос создает операторы 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)

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

Скотт Аллен
источник
3

Намного проще (и, возможно, даже быстрее) составить сценарий для вашей базы данных, а затем просто удалить и создать ее из сценария.

Аляска
источник
3

Создайте пустую «шаблонную» базу данных, сделайте полную резервную копию. Когда вам нужно обновить, просто восстановите, используя WITH REPLACE. Быстро, просто, пуленепробиваемо. И если пара таблиц здесь или там нуждается в каких-то базовых данных (например, информация о конфигурации или просто базовая информация, которая заставляет ваше приложение работать), то это тоже обрабатывается.

onupdatecascade
источник
2

Это один из способов сделать это ... вероятно, есть еще 10, которые лучше / эффективнее, но, похоже, это делается очень редко, так что вот так ...

получить список tablesиз sysobjects, а затем перебрать тех, с курсором, вызывая sp_execsql('truncate table ' + @table_name)для каждого iteration.

Бен Шейрман
источник
добавил пост с sql, который делает именно это :), так как это было то, что я тоже искал.
Крис Смит
1

Запустите закомментированный раздел один раз, заполните таблицу _TruncateList таблицами, которые вы хотите усечь, а затем запустите остальную часть сценария. Таблицу _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
Стив Худ
источник
0

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

Это или сохранить резервную копию вашей пустой БД и восстановить ее поверх старой

Брайан Спенсер
источник
2
Причина в том, что загрузка и воссоздание файлов, журналов и т. Д. Базы данных происходит очень медленно. Подумайте, протирая базу данных 1000 раз во время приличного модульного тестирования.
Крис К.Л.
0

Перед усечением таблиц необходимо удалить все внешние ключи. Используйте этот скрипт для генерации финальных скриптов для удаления и воссоздания всех внешних ключей в базе данных. Пожалуйста, установите для переменной @action значение 'CREATE' или 'DROP'.

Эдвард Вайнерт
источник
0

выберите «удалить из» + TABLE_NAME из INFORMATION_SCHEMA.TABLES, где TABLE_TYPE = «BASE TABLE»

где результат пришел.

Скопируйте и вставьте в окно запроса и выполните команду

Сомендра Тивари
источник
0

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

  1. Сохранить все ограничения во временной таблице
  2. Отбросьте все ограничения
  3. Обрезать все таблицы, за исключением некоторых таблиц, которые не требуют усечения
  4. Воссоздайте все ограничения.

Я перечислил это в своем блоге здесь

Мохит
источник