Bagaimana Anda memotong semua tabel dalam database menggunakan TSQL?


204

Saya memiliki lingkungan pengujian untuk database yang ingin saya muat ulang dengan data baru pada awal siklus pengujian. Saya tidak tertarik membangun kembali seluruh basis data - cukup dengan "mengatur ulang" data.

Apa cara terbaik untuk menghapus semua data dari semua tabel menggunakan TSQL? Apakah ada prosedur, tampilan, dll. Yang dapat disimpan sistem? Saya tidak ingin secara manual membuat dan memelihara pernyataan tabel truncate untuk setiap tabel - saya lebih suka itu dinamis.

Jawaban:


188

Untuk SQL 2005,

EXEC sp_MSForEachTable 'TRUNCATE TABLE ?'

Pasangan lebih banyak tautan untuk 2000 dan 2005/2008 ..


62
Anda tidak dapat memotong tabel yang memiliki kunci asing, jadi ini hanya akan berfungsi jika tidak ada batasan kunci asing di antara tabel (atau mereka telah dinonaktifkan).
marcj

1
setuju..saya pikir karena dia secara khusus meminta memotong meja, dia sudah memecahkan masalah dengan kunci asing ..
Gulzar Nazim

@ gulzar- semacam- saya memposting pertanyaan terpisah tentang bagaimana menangani FK tetapi jawaban Anda berdiri pada kemampuannya sendiri.
Ray

11
@ Sam: Tidak, tidak akan! Data dalam tabel tidak relevan. Selama ada batasan kunci asing referensi tabel (bahkan yang dinonaktifkan) Anda tidak dapat memotongnya.
TToni

3
'EXEC sp_MSForEachTable' DROP TABLE? ' Bekerja dengan baik :) (memuaskan semua tabel dari basis data)
kuncevic.dev

419

Ketika berhadapan dengan menghapus data dari tabel yang memiliki hubungan kunci asing - yang pada dasarnya merupakan kasus dengan database yang dirancang dengan benar - kita dapat menonaktifkan semua kendala, menghapus semua data dan kemudian mengaktifkan kembali kendala

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

Lebih lanjut tentang menonaktifkan batasan dan pemicu di sini

jika beberapa tabel memiliki kolom identitas, kami mungkin ingin me-reseednya

EXEC sp_MSForEachTable "DBCC CHECKIDENT ( '?', RESEED, 0)"

Perhatikan bahwa perilaku RESEED berbeda antara tabel baru, dan yang sebelumnya memiliki beberapa data yang disisipkan dari BOL :

DBCC CHECKIDENT ('table_name', RESEED, newReseedValue)

Nilai identitas saat ini diatur ke newReseedValue. Jika tidak ada baris yang dimasukkan ke tabel sejak dibuat, baris pertama yang dimasukkan setelah mengeksekusi DBCC CHECKIDENT akan menggunakan newReseedValue sebagai identitas. Jika tidak, baris berikutnya yang dimasukkan akan menggunakan newReseedValue + 1. Jika nilai newReseedValue kurang dari nilai maksimum di kolom identitas, pesan kesalahan 2627 akan dihasilkan pada referensi selanjutnya ke tabel.

Terima kasih kepada Robert karena menunjukkan fakta bahwa penonaktifan kendala tidak memungkinkan untuk menggunakan truncate, kendala harus dijatuhkan, dan kemudian diciptakan kembali


34
Menonaktifkan batasan tidak akan memungkinkan pemotongan tabel yang direferensikan oleh batasan ASING. Batasan FK harus dijatuhkan. Harap balas jika saya salah tentang ini, tetapi saya tidak menemukan cara untuk menghindari menjatuhkan mereka.
Robert Claypool

1
Hanya kesalahan ketik "Tabel" yang seharusnya tidak ada dalam pernyataan ini EXEC sp_MSForEachTable "HAPUS DARI TABEL?" Versi yang benar harus: EXEC sp_MSForEachTable "DELETE FROM?"
Raghav

4
Jika Anda menggunakan 2008 atau SSMS yang lebih baru, Anda mungkin ingin menambahkan SET ROWCOUNT 0di awal skrip Anda karena defaultnya adalah membatasi aksi hingga 500 baris! Anda akan mendapatkan kesalahan yang membuat frustrasi seperti yang saya lakukan karena tidak semua data benar-benar telah dihapus.
Sean Hanley

1
Ini bekerja dengan baik. Dalam kasus saya, saya juga harus menambahkan EXEC sp_msforeachtable "ALTER TABLE? Nonaktifkan TRIGGER all" dan EXEC sp_msforeachtable "ALTER TABLE? Aktifkan TRIGGER all" Sebelum dan sesudah pernyataan hapus.
RobC

2
Jawaban favorit saya Tapi mengapa Anda (semua, bahkan komentator) melampirkan string SQL literal dalam tanda kutip ganda?
bitoolean

57

Inilah raja ayah dari skrip penghapusan database. Ini akan menghapus semua tabel dan memasangnya kembali dengan benar:

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;

Nikmati, tapi hati-hati!


2
Sayangnya perintah di atas gagal jika Anda telah menghitung kolom, karena sp_MSforeachtable ternyata ada SET QUOTED_IDENTITY OFFdi badannya ( tautan ). UPDATE: Perbaikannya adalah menambahkan "SET QUOTED_IDENTIFIERS on;" di awal setiap pernyataan yang memunculkan kesalahan ini (sebagaimana disebutkan di sini )
Marchy

1
sepertinya ini tidak
mengembalikan

48

Cara paling sederhana untuk melakukan ini adalah dengan

  1. buka SQL Management Studio
  2. arahkan ke basis data Anda
  3. Klik kanan dan pilih Tugas-> Hasilkan Skrip (gambar 1)
  4. Pada layar "pilih Objek", pilih opsi "pilih objek spesifik" dan centang "tabel" (gambar 2)
  5. pada layar berikutnya, pilih "lanjutan" dan kemudian ubah opsi "Script DROP and CREATE" menjadi "Script DROP and CREATE" (pic 3)
  6. Pilih untuk menyimpan skrip ke jendela editor baru atau file dan jalankan seperlunya.

ini akan memberi Anda skrip yang menghapus dan membuat ulang semua tabel Anda tanpa perlu khawatir tentang debugging atau apakah Anda sudah memasukkan semuanya. Meskipun ini melakukan lebih dari sekedar pemotongan, hasilnya sama. Hanya perlu diingat bahwa kunci primer peningkatan-otomatis Anda akan mulai dari 0, tidak seperti tabel terpotong yang akan mengingat nilai terakhir yang diberikan. Anda juga dapat menjalankan ini dari kode jika Anda tidak memiliki akses ke studio Manajemen di lingkungan PreProd atau Produksi Anda.

1.

masukkan deskripsi gambar di sini

2.

masukkan deskripsi gambar di sini

3.

masukkan deskripsi gambar di sini


1
Perhatian jika Anda menggunakan ini untuk database dengan skema yang sangat kompleks. Saya mencobanya pada salinan dev dari DB produksi kami dan itu menghancurkan skema, membutuhkan pengerahan kembali total.
Techrocket9

1
Anda harus membuat skrip semua hal yang ingin Anda pertahankan
Kapten Kenpachi

13

Memotong semua tabel hanya akan berfungsi jika Anda tidak memiliki hubungan kunci asing antara tabel Anda, karena SQL Server tidak akan memungkinkan Anda untuk memotong tabel dengan kunci asing.

Alternatif untuk ini adalah dengan menentukan tabel dengan kunci asing dan menghapusnya terlebih dahulu, Anda kemudian dapat memotong tabel tanpa kunci asing sesudahnya.

Lihat http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=65341 dan http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=72957 untuk detail lebih lanjut.


1
Poin yang bagus. Tidak memikirkan itu. Saya mungkin dapat menonaktifkan semua kendala pertama dan kemudian mengaktifkannya kembali setelah data telah dihapus.
Ray

7

Opsi alternatif yang ingin saya gunakan dengan MSSQL Server Deveploper atau Enterprise adalah membuat snapshot dari database segera setelah membuat skema kosong. Pada titik itu Anda bisa terus mengembalikan database ke snapshot.


Sayangnya Anda kehilangan semua indeks FULLTEXT Anda setiap kali
Chris KL

6

Jangan lakukan ini! Sungguh, bukan ide yang bagus.

Jika Anda tahu tabel mana yang ingin Anda potong, buat prosedur tersimpan yang memotongnya. Anda dapat memperbaiki urutan untuk menghindari masalah kunci asing.

Jika Anda benar-benar ingin memotong semuanya (sehingga Anda dapat BCP memuatnya misalnya), Anda akan dengan cepat menjatuhkan database dan membuat yang baru dari awal, yang akan memiliki manfaat tambahan yang Anda tahu persis di mana Anda berada.


Pendekatan alternatif yang bagus di sini.
Sam

3
masalah dengan pendekatan Anda adalah bahwa menjatuhkan tabel dan basis data akan mengakibatkan kehilangan semua izin yang diberikan ke info masuk dan skema yang berbeda. Untuk membuat ulang itu akan menyakitkan untuk database besar dengan banyak tabel.
Punit Vora

4

Jika Anda ingin menyimpan data dalam tabel tertentu (yaitu tabel pencarian statis) saat menghapus / memotong data dalam tabel lain dalam db yang sama, maka Anda perlu loop dengan pengecualian di dalamnya. Inilah yang saya cari ketika saya menemukan pertanyaan ini.

sp_MSForEachTable tampaknya buggy bagi saya (yaitu perilaku tidak konsisten dengan pernyataan IF) yang mungkin mengapa tidak didokumentasikan oleh 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

Bagian tersulit dari memotong semua tabel adalah menghapus dan memasang kembali batasan kunci asing.

Query berikut membuat pernyataan drop & create untuk setiap kendala yang terkait dengan setiap nama tabel di @myTempTable. Jika Anda ingin membuat ini untuk semua tabel, Anda dapat menggunakan skema informasi sederhana untuk mengumpulkan nama-nama tabel ini sebagai gantinya.

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)

Saya kemudian hanya menyalin pernyataan untuk menjalankan - tetapi dengan sedikit usaha dev Anda bisa menggunakan kursor untuk menjalankannya secara dinamis.


3

Jauh lebih mudah (dan mungkin bahkan lebih cepat) untuk skrip keluar dari database Anda, lalu lepaskan dan buat dari skrip.


3

Buat database "templat" kosong, ambil cadangan penuh. Saat Anda perlu menyegarkan, cukup kembalikan menggunakan WITH REPLACE. Cepat, sederhana, antipeluru. Dan jika beberapa tabel di sini atau di sana memerlukan beberapa data dasar (mis. Informasi konfigurasi, atau hanya informasi dasar yang membuat aplikasi Anda berjalan) itu juga menangani itu.


2

Ini adalah salah satu cara untuk melakukannya ... kemungkinan ada 10 lainnya yang lebih baik / lebih efisien, tetapi sepertinya ini dilakukan sangat jarang, jadi begini ...

dapatkan daftar tablesdari sysobjects, lalu lilitkan dengan kursor, yang memanggil sp_execsql('truncate table ' + @table_name)masing-masing iteration.


menambahkan posting dengan sql yang melakukan ini :) karena ini adalah apa yang saya cari juga.
Chris Smith

1

Jalankan bagian yang dikomentari satu kali, isi tabel _TruncateList dengan tabel yang ingin Anda potong, kemudian jalankan sisa skrip. Tabel _ScriptLog perlu dibersihkan dari waktu ke waktu jika Anda sering melakukan ini.

Anda dapat memodifikasi ini jika Anda ingin melakukan semua tabel, cukup masukkan nama SELECT KE #TruncateList FROM sys.tables. Namun, Anda biasanya tidak ingin melakukan semuanya.

Selain itu, ini akan memengaruhi semua kunci asing di basis data, dan Anda dapat memodifikasinya juga jika terlalu tumpul untuk aplikasi Anda. Ini bukan untuk tujuan saya.

/*
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

Saya tidak melihat mengapa menghapus data akan lebih baik daripada skrip untuk menjatuhkan dan membuat kembali setiap tabel.

Itu atau simpan cadangan DB kosong Anda dan kembalikan ke yang lama


2
Alasannya adalah overhead menjatuhkan dan menciptakan kembali database pada file-file, log, dll sangat lambat. Pikirkan menyeka database 1.000 kali selama menjalankan pengujian unit yang layak.
Chris KL

0

Sebelum memotong tabel, Anda harus menghapus semua kunci asing. Gunakan skrip ini untuk menghasilkan skrip final untuk menjatuhkan dan membuat ulang semua kunci asing dalam database. Harap atur variabel @action ke 'CREATE' atau 'DROP'.


0

pilih 'delete from' + TABLE_NAME dari INFORMATION_SCHEMA.TABLES di mana TABLE_TYPE = 'BASE TABLE'

dari mana datangnya hasil

Salin dan tempel pada jendela permintaan dan jalankan perintah


0

Agak terlambat tetapi mungkin bisa membantu seseorang. Saya membuat prosedur kadang-kadang kembali yang melakukan hal berikut ini menggunakan T-SQL:

  1. Simpan semua kendala dalam tabel Sementara
  2. Jatuhkan Semua Kendala
  3. Pangkas semua tabel dengan pengecualian beberapa tabel, yang tidak perlu dipotong
  4. Buat kembali semua Kendala.

Saya telah mendaftarkannya di blog saya di sini

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.