Як обрізати всі таблиці в базі даних за допомогою TSQL?


204

У мене є тестове середовище для бази даних, яку я хочу перезавантажити новими даними на початку тестового циклу. Мені не цікаво відновлювати всю базу даних - просто просто «перевстановлюючи» дані.

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

Відповіді:


188

Для SQL 2005 року

EXEC sp_MSForEachTable 'TRUNCATE TABLE ?'

Ще кілька посилань на 2000 та 2005/2008 роки .


62
Ви не можете вкорочувати таблиці, у яких є сторонні ключі, тому це буде працювати лише в тому випадку, якщо між таблицями немає обмежень для зовнішніх ключів (або вони були відключені).
marcj

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

@ gulzar- начебто я розмістив окреме запитання про те, як поводитися з ФК, але ваша відповідь стоїть на власних заслугах.
Рей

11
@Sam: Ні, не буде! Дані в таблицях не мають значення. Поки існує стороннє ключове обмеження, на яке посилається таблиця (навіть інвалід), ви не можете її усікати.
TToni

3
'EXEC sp_MSForEachTable' ЗРОЗНІТЬ ТАБЛИЦЮ? ' Також чудово працює :) (захоплюючи всі таблиці з бази даних)
kuncevic.dev

418

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

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

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

Дякуючи Роберту за те, що він вказав на те, що відключення обмежень не дозволяє використовувати скорочення, обмеження повинні бути скасовані, а потім відтворені


34
Якщо вимкнути обмеження, НЕ дозволять усікати таблиці, на які посилається обмеження FOREIGN KEY. Обмеження ФК необхідно скасувати. Будь ласка, відповідайте, якщо я помиляюся з цього приводу, але я не знайшов способу уникнути їх відкидання.
Роберт Клейпул

1
У цьому вислові не повинно бути ключового слова "Таблиця". EXEC sp_MSForEachTable "ВИДАЛИТИ З ТАБЛИЦІ?" .Коректна версія повинна бути: EXEC sp_MSForEachTable "ВІДКРИТИ З?"
Рагхав

4
Якщо ви використовуєте 2008 або новіші SSMS, ви, ймовірно, захочете додати SET ROWCOUNT 0на початку сценарію, оскільки за замовчуванням обмежувати дії до 500 рядків! Ви отримаєте неприємні помилки, як я, тому що не всі дані фактично були видалені.
Шон Хенлі

1
Це спрацювало чудово. У моєму випадку мені також довелося додати EXEC sp_msforeachtable "ALTER TABLE? Disable TRIGGER all" та EXEC sp_msforeachtable "ALTER TABLE? Enable TRIGGER all" до та після оператора видалення.
RobC

2
Моя улюблена відповідь. Але чому ви (усі, навіть коментатори) вкладаєте буквальні рядки SQL у подвійні лапки?
Битол

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"; " на початку кожного твердження, яке викликає цю помилку (про що згадувалося тут )
березень

1
схоже, це не переосмислило мої посвідчення
totooooo

48

Найпростіший спосіб зробити це

  1. відкрити SQL Management Studio
  2. перейдіть до вашої бази даних
  3. Клацніть правою кнопкою миші та виберіть Завдання-> Створити сценарії (мал. 1)
  4. На екрані "вибрати об'єкти" виберіть параметр "вибрати конкретні об'єкти" і поставте галочку "таблиці" (рис. 2)
  5. на наступному екрані виберіть "розширений", а потім змініть параметр "Скрипт DROP and CREATE" на "DROP і СТВОРИТИ Сценарій" (мал. 3)
  6. Виберіть для збереження сценарій у новому вікні редактора або файл та запустіть за необхідності.

це дасть вам сценарій, який скидає та відтворює всі ваші таблиці без необхідності турбуватися про налагодження чи чи все ви включили. Хоча це виконує більше, ніж просто усікання, результати однакові. Пам’ятайте лише про те, що ваші первинні ключі з автоматичним збільшенням починатимуться з 0, на відміну від усічених таблиць, які запам’ятають останнє призначене значення. Ви також можете виконати це з коду, якщо у вас немає доступу до студії управління у ваших середовищах PreProd або Production.

1.

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

2.

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

3.

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


1
Обережно, якщо ви використовуєте це для бази даних з дуже складною схемою. Я спробував це на розробленій копії нашого виробничого БД, і він зруйнував схему, вимагаючи повного повторного використання.
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 .


1
Гарна думка. Не думав про це. Можливо, я можу спочатку відключити всі обмеження, а потім повторно увімкнути їх, як тільки дані будуть видалені.
Рей

7

Альтернативний варіант, який я люблю використовувати з MSSQL Server Deveploper або Enterprise, - це створити знімок бази даних відразу після створення порожньої схеми. У цей момент ви можете просто відновити базу даних до знімка.


На жаль, ви щоразу втрачаєте всі свої індекси FULLTEXT
Chris KL

6

Не робіть цього! Дійсно, не гарна ідея.

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

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


Приємний альтернативний підхід тут.
Сем

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

4

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

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

Найважчою частиною обрізання всіх таблиць є видалення та повторне введення обмежень із зовнішнього ключа.

Наступний запит створює падіння & створення операторів для кожного обмеження, що стосується кожного імені таблиці в @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

Складіть порожню базу даних «шаблон», візьміть повну резервну копію. Коли вам потрібно оновити, просто відновіть за допомогою ЗАМІНИ. Швидкий, простий, куленепроникний. І якщо для кількох таблиць тут або там потрібні деякі базові дані (наприклад, інформація про конфігурацію або просто основна інформація, яка змушує ваш додаток працювати), він теж обробляє це.


2

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

отримати список tablesвід sysobjects, а потім перевести цикл на тих, хто має курсор, закликаючи sp_execsql('truncate table ' + @table_name)кожного iteration.


додав допис з sql, який робить саме це :), оскільки це було те, що я теж шукав.
Кріс Сміт

1

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

Ви можете змінити це, якщо ви хочете робити всі таблиці, просто введіть SELECT ім'я INTO #TruncateList ВІД 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. Відтворити всі обмеження.

Я перерахував це у своєму блозі тут

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