Saya memiliki tabel data besar. Ada 10 juta catatan dalam tabel ini.
Apa cara terbaik untuk kueri ini
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Saya memiliki tabel data besar. Ada 10 juta catatan dalam tabel ini.
Apa cara terbaik untuk kueri ini
Delete LargeTable where readTime < dateadd(MONTH,-7,GETDATE())
Jawaban:
Jika Anda menghapus Semua baris dalam tabel itu, pilihan paling sederhana adalah memotong tabel, kira-kira seperti itu
TRUNCATE TABLE LargeTable
GO
Truncate table hanya akan mengosongkan tabel, Anda tidak dapat menggunakan klausa WHERE untuk membatasi baris yang dihapus dan tidak ada pemicu yang akan dipecat.
Di sisi lain jika Anda menghapus lebih dari 80-90 Persen data, katakanlah jika Anda memiliki total 11 Juta baris dan Anda ingin menghapus 10 juta cara lain adalah dengan Memasukkan 1 juta baris ini (catatan yang ingin Anda pertahankan) ) ke meja pementasan lain. Pangkas tabel besar ini dan masukkan kembali 1 Juta baris ini.
Atau jika izin / tampilan atau objek lain yang memiliki tabel besar ini sebagai tabel yang mendasarinya tidak terpengaruh dengan menjatuhkan tabel ini, Anda bisa mendapatkan jumlah baris yang relatif kecil ini ke tabel lain letakkan tabel ini dan buat tabel lain dengan skema yang sama dan impor ini baris kembali ke tabel ex-Large ini.
Satu opsi terakhir yang bisa saya pikirkan adalah mengubah database Anda Recovery Mode to SIMPLE
dan kemudian menghapus baris dalam batch yang lebih kecil menggunakan loop sementara seperti ini ..
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
END
dan jangan lupa untuk mengubah mode Pemulihan kembali ke penuh dan saya pikir Anda harus mengambil cadangan untuk membuatnya sepenuhnya efektif (perubahan atau mode pemulihan).
optimal solution for unknown case
itulah mimpinya bukan? Sayangnya Anda tidak dapat menyembuhkan setiap penyakit dengan satu pil; Saya telah menyarankan beberapa solusi yang mungkin untuk skenario yang berbeda. Sayangnya tidak ada peluru keras di sini.
@ m-ali jawaban benar tetapi juga perlu diingat bahwa log dapat tumbuh banyak jika Anda tidak melakukan transaksi setelah setiap chunk dan melakukan pos pemeriksaan. Ini adalah bagaimana saya akan melakukannya dan mengambil artikel ini http://sqlperformance.com/2013/03/io-subsystem/chunk-deletes sebagai referensi, dengan tes kinerja dan grafik:
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
BEGIN TRANSACTION
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
COMMIT TRANSACTION
CHECKPOINT -- for simple recovery model
END
COMMIT TRANSACTION
dan CHECKPOINT
log masih terus tumbuh. Terima kasih telah menjelaskan ini.
@Deleted_Rows
dengan 10.000 atau Anda mungkin berakhir dengan loop tak terhingga karena menghapus data yang tidak terbatas. Jadi WHILE (@Deleted_Rows = 10000)
- segera setelah tidak ada "halaman" data lengkap untuk menghapusnya akan berhenti. Dalam implementasi Anda,, WHILE (@Deleted_Rows > 0)
loop-sementara akan mengeksekusi kembali bahkan jika itu hanya menghapus satu baris, dan eksekusi berikutnya mungkin juga menemukan satu atau dua baris untuk dihapus - menghasilkan loop tak terbatas.
WHILE
lingkaran itu sendiri: dateadd(MONTH,-7,GETDATE())
.
WHILE
loop.
Anda juga dapat menggunakan GO + berapa kali Anda ingin menjalankan kueri yang sama.
DELETE TOP (10000) [TARGETDATABASE].[SCHEMA].[TARGETTABLE]
WHERE readTime < dateadd(MONTH,-1,GETDATE());
-- how many times you want the query to repeat
GO 100
GO xx
seharusnya berfungsi? Saya mendapatkan kesalahan "Tidak dapat menemukan prosedur tersimpan ''" . Tanpa GO
perintah itu berfungsi dengan baik.
@Francisco Goldenstein, hanya koreksi kecil. COMMIT harus digunakan setelah Anda mengatur variabel, jika tidak, WHILE akan dijalankan sekali saja:
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
BEGIN TRANSACTION
-- Delete some small number of rows at a time
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
SET @Deleted_Rows = @@ROWCOUNT;
COMMIT TRANSACTION
CHECKPOINT -- for simple recovery model
END
Variasi dari M.Ali ini bekerja dengan baik untuk saya. Menghapus beberapa, menghapus log dan mengulangi. Saya melihat log tumbuh, turun, dan mulai lagi.
DECLARE @Deleted_Rows INT;
SET @Deleted_Rows = 1;
WHILE (@Deleted_Rows > 0)
BEGIN
-- Delete some small number of rows at a time
delete top (100000) from InstallLog where DateTime between '2014-12-01' and '2015-02-01'
SET @Deleted_Rows = @@ROWCOUNT;
dbcc shrinkfile (MobiControlDB_log,0,truncateonly);
END
# of rows
untuk menghapus sekaligus, dan juga WHERE
klausa. Bekerja seperti pesona!
Jika Anda bersedia (dan mampu) mengimplementasikan partisi, itu adalah teknik yang efektif untuk menghapus sejumlah besar data dengan sedikit overhead run-time. Namun, tidak hemat biaya untuk latihan sekali saja.
Saya dapat menghapus 19 juta baris dari meja saya yang terdiri dari 21 juta baris dalam hitungan menit . Ini pendekatan saya.
Jika Anda memiliki kunci primer peningkatan-otomatis pada tabel ini, maka Anda dapat menggunakan kunci utama ini.
Dapatkan nilai minimum kunci utama dari tabel besar tempat readTime <dateadd (MONTH, -7, GETDATE ()). (Tambahkan indeks pada readTime, jika belum ada, indeks ini akan tetap dihapus bersama dengan tabel di langkah 3.). Mari kita simpan dalam variabel 'min_primary'
Masukkan semua baris yang memiliki kunci utama> min_primary ke dalam tabel pementasan (tabel memori jika jumlah baris tidak besar).
Jatuhkan meja besar.
Buat ulang meja. Salin semua baris dari staging table ke tabel utama.
Jatuhkan meja pementasan.
Anda dapat menghapus kumpulan kecil menggunakan loop sementara, sesuatu seperti ini:
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
WHILE @@ROWCOUNT > 0
BEGIN
DELETE TOP (10000) LargeTable
WHERE readTime < dateadd(MONTH,-7,GETDATE())
END
Penggunaan lain:
SET ROWCOUNT 1000 -- Buffer
DECLARE @DATE AS DATETIME = dateadd(MONTH,-7,GETDATE())
DELETE LargeTable WHERE readTime < @DATE
WHILE @@ROWCOUNT > 0
BEGIN
DELETE LargeTable WHERE readTime < @DATE
END
SET ROWCOUNT 0
Pilihan;
Jika log transaksi diaktifkan, nonaktifkan log transaksi.
ALTER DATABASE dbname SET RECOVERY SIMPLE;
Jika Anda menggunakan SQL server 2016 atau lebih tinggi dan jika tabel Anda memiliki partisi yang dibuat berdasarkan kolom yang Anda coba hapus (misalnya kolom Timestamp), maka Anda bisa menggunakan perintah baru ini untuk menghapus data dengan partisi.
TULANG MEJA DENGAN (PARTISI ({|} [, ... n]))
Ini akan menghapus data hanya di partisi yang dipilih dan harus menjadi cara paling efisien untuk menghapus data dari bagian tabel karena tidak akan membuat log transaksi dan akan dilakukan secepat pemotongan biasa tetapi tanpa menghapus semua data dari meja.
Kekurangannya adalah jika meja Anda tidak disetel dengan partisi, maka Anda harus menggunakan jadul dan menghapus data dengan pendekatan reguler dan kemudian membuat ulang tabel dengan partisi sehingga Anda bisa melakukan ini di masa depan, itulah yang saya lakukan. Saya menambahkan pembuatan partisi dan penghapusan ke dalam prosedur penyisipan itu sendiri. Saya punya meja dengan 500 juta baris jadi ini adalah satu-satunya pilihan untuk mengurangi waktu penghapusan.
Untuk detail lebih lanjut lihat tautan di bawah ini: https://docs.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-2017
SQL server 2016 Tabel terpotong dengan partisi
Di bawah ini adalah apa yang saya lakukan pertama kali untuk menghapus data sebelum saya bisa membuat ulang tabel dengan partisi dengan data yang diperlukan di dalamnya. Kueri ini akan berjalan selama berhari-hari selama jendela waktu yang ditentukan hingga data dihapus.
:connect <<ServerName>>
use <<DatabaseName>>
SET NOCOUNT ON;
DECLARE @Deleted_Rows INT;
DECLARE @loopnum INT;
DECLARE @msg varchar(100);
DECLARE @FlagDate datetime;
SET @FlagDate = getdate() - 31;
SET @Deleted_Rows = 1;
SET @loopnum = 1;
/*while (getdate() < convert(datetime,'2018-11-08 14:00:00.000',120))
BEGIN
RAISERROR( 'WAIT for START' ,0,1) WITH NOWAIT
WAITFOR DELAY '00:10:00'
END*/
RAISERROR( 'STARTING PURGE' ,0,1) WITH NOWAIT
WHILE (1=1)
BEGIN
WHILE (@Deleted_Rows > 0 AND (datepart(hh, getdate() ) >= 12 AND datepart(hh, getdate() ) <= 20)) -- (getdate() < convert(datetime,'2018-11-08 19:00:00.000',120) )
BEGIN
-- Delete some small number of rows at a time
DELETE TOP (500000) dbo.<<table_name>>
WHERE timestamp_column < convert(datetime, @FlagDate,102)
SET @Deleted_Rows = @@ROWCOUNT;
WAITFOR DELAY '00:00:01'
select @msg = 'ROWCOUNT' + convert(varchar,@Deleted_Rows);
set @loopnum = @loopnum + 1
if @loopnum > 1000
begin
begin try
DBCC SHRINKFILE (N'<<databasename>>_log' , 0, TRUNCATEONLY)
RAISERROR( @msg ,0,1) WITH NOWAIT
end try
begin catch
RAISERROR( 'DBCC SHRINK' ,0,1) WITH NOWAIT
end catch
set @loopnum = 1
end
END
WAITFOR DELAY '00:10:00'
END
select getdate()
Jika saya katakan tanpa loop, saya bisa menggunakan GOTO
pernyataan untuk menghapus sejumlah besar catatan menggunakan sql server. exa.
IsRepeat:
DELETE TOP (10000)
FROM <TableName>
IF @@ROWCOUNT > 0
GOTO IsRepeat
seperti cara ini Anda dapat menghapus sejumlah besar data dengan ukuran penghapusan yang lebih kecil.
beri tahu saya jika memerlukan informasi lebih lanjut.