We have a table in that daily around we are getting 1 M records. In this table we are Parsing 7 days old data. Almost we are deleting 1 M records as well. One of my junior team members has written the code and testing it in the small dataset and it was working fine. When we had deployed in upper environment our query blocks everything… log file grows… tempdb cries…. QA team reach out to use saying everything got hanged. Could you please have a look.
When we saw the code, he has used
CTE to delete the records. I asked some other junior team member the said that
sir I will use “Delete
from table where column condition”. I asked him to try this. He tried
after some time he came back to me and saying this this is also not working.
When we are running the delete command our other query which used this table
getting slow and, in the application, getting failed due to time out issue.
Here we will see the solution how
we will come over from this issue.
Ø Instead of CTE we will use Temp table
Ø Instead of Deleting the All records at a time we will delete these records into a batch
Here we will see when we use CTE and Temp table. Let’s break it with real examples, performance impact, and best practices.
Demo…
We have a table
|
--creating a table and inserting records create table Sales_order( SO_id bigint identity(1,1), So_name varchar(50), Qntity decimal(18,10), create_user varchar(50) default current_user, create_dt datetime default getdate()) |
First we insert 1 M record having
8 day old date as created date form this demo
|
--Inserting record 1 M DECLARE @c bigint, @create_dt datetime =getdate()-8 set @c=0 while @c<=1000000 BEGIN insert into Sales_order(So_name,Qntity,create_dt) select 'So_name'+cast(@c as varchar(10)) as So_name, @c* Rand(),@create_dt set @c=@c+1 END |
|
--Inserting record 1 M DECLARE @c bigint, @create_dt datetime =getdate()-2 set @c=0 while @c<=1000000 BEGIN insert into Sales_order(So_name,Qntity,create_dt) select 'So_name'+cast(@c as varchar(10)) as So_name, @c* Rand(),@create_dt set @c=@c+1 END --Inserting record 1 M DECLARE @c bigint, @create_dt datetime =getdate() set @c=0 while @c<=1000000 BEGIN insert into Sales_order(So_name,Qntity,create_dt) select 'So_name'+cast(@c as varchar(10)) as So_name, @c* Rand(),@create_dt set @c=@c+1 END |
“First, let’s see how DELETE
works with a CTE. A CTE is just a logical result set – it does not
store data physically.”
DELETE Using CTE
|
WITH cte_Sales_order AS ( SELECT * FROM Sales_order WHERE create_dt <
(GETDATE()-7) ) select count(*) FROM cte_Sales_order; |
Seeing the result
Now we are deleting these records
Before deleting these records.
“What’s happening here?
Ø CTE is evaluated
Ø Rows are identified
Ø DELETE executes immediately
BUT remember…”
CTE is re-evaluated every time
Ø No indexing
Ø No statistics
Ø Limited control over execution
Ø Poor choice for very large deletes
If the query plan changes, performance changes!”
|
WITH
cte_Sales_order AS ( SELECT * FROM Sales_order WHERE create_dt < (GETDATE()-7) ) delete
FROM cte_Sales_order; |
For the next demo we are inserting 1 M records
|
--Inserting
record 1 M DECLARE @c bigint, @create_dt
datetime =getdate()-8 set @c=0 while @c<=1000000 BEGIN insert into Sales_order(So_name,Qntity,create_dt) select
'So_name'+cast(@c as varchar(10)) as So_name, @c*
Rand(),@create_dt set @c=@c+1 END |
|
DELETE FROM Sales_order WHERE create_dt < (GETDATE()-7) |
This also delete the records.
Problem of this as below
Ø Long blocking
Ø Transaction log explosion
Ø Possible lock escalation (table lock)
Ø Rollback risk if it fails
Ø Can bring production to a halt
Now the Best we to use Batch
Delete using TOP or using Temp table and the delete it in a batch
For this demo inserting again 1 M
records.
Read here : Deleting Records
from Large tables (Batch Delete)
https://bageshkumarbagi-msbi.blogspot.com/2017/07/deleting-records-from-large-tables.html
here we are using temp table to
delete the records.
First of all we will create a
temp table and store the ID which records we are going to delete. See below.
|
CREATE TABLE #DelRecords (id bigint primary key not null) insert into #DelRecords(id) SELECT SO_id FROM Sales_order WHERE create_dt <
(GETDATE()-7); DECLARE @batchSize INT ; set @batchSize = 10000; BEGIN DELETE TOP (@batchSize) so FROM Sales_order so join #DelRecords
dr on so.SO_id=dr.id IF @@ROWCOUNT = 0 BREAK; -- Optional pause to reduce
pressure WAITFOR DELAY '00:00:01'; END |
Benefits:
Ø Small
transactions
Ø Minimal
blocking
Ø Log
growth under control
Ø Safe
for production
While we are deleting large
volume of data, we need to keep in mind
What NOT to do
Ø Single large DELETE
Ø Delete without WHERE clause
Ø Delete without index
Ø Run during peak business hours
“To delete millions of records
safely in SQL Server, I always use batch deletes using TOP or key ranges to
avoid blocking, log growth, and performance issues.”
No comments:
Post a Comment
If you have any doubt, please let me know.