Here is my SQL Query. It's insert almost 6500+ row from temp table. But its takes 15+ mins! . How can i improve this ? Thanks
ALTER proc [dbo].[Process_bill]
@userid varchar(10),
@remark nvarchar(500),
@tdate date ,
@pdate date
as
BEGIN
IF OBJECT_ID('tempdb.dbo..#temptbl_bill', 'U') IS NOT NULL
DROP TABLE #temptbl_bill;
CREATE TABLE #temptbl_bill (
RowID int IDENTITY(1, 1),
------------
)
// instert into temp table
DECLARE @NumberRecords int, @RowCounter int
DECLARE @batch INT
SET @batch = 300
SET @NumberRecords = (SELECT COUNT(*) FROM #temptbl_bill)
SET @RowCounter = 1
SET NOCOUNT ON
BEGIN TRANSACTION
WHILE @RowCounter <= @NumberRecords
BEGIN
declare @clid int
declare @hlid int
declare @holdinNo nvarchar(150)
declare @clientid nvarchar(100)
declare @clientName nvarchar(50)
declare @floor int
declare @radius nvarchar(50)
declare @bill money
declare @others money
declare @frate int
declare @due money
DECLARE @fine money
DECLARE @rebate money
IF @RowCounter > 0 AND ((@RowCounter % @batch = 0) OR (@RowCounter = @NumberRecords))
BEGIN
COMMIT TRANSACTION
PRINT CONCAT('Transaction #', CEILING(@RowCounter/ CAST(@batch AS FLOAT)), ' committed (', @RowCounter,' rows)');
BEGIN TRANSACTION
END;
// multiple select
// insert to destination table
Print 'RowCount -' +cast(@RowCounter as varchar(20)) + 'batch -' + cast(@batch as varchar(20))
SET @RowCounter = @RowCounter + 1;
END
COMMIT TRANSACTION
PRINT CONCAT('Transaction #', CEILING(@RowCounter/ CAST(@batch AS FLOAT)), ' committed (',
@RowCounter,' rows)');
SET NOCOUNT OFF
DROP TABLE #temptbl_bill
END
GO
As has been said in comments, the loop is completely unnecessary. The way to improve the performance of any loop is to remove it completely. Loops are a last resort in SQL.
As far as I can tell your insert can be written with a single statement:
Please take this with a pinch of salt, it was quite hard work trying to piece together the logic, so it may need some minor tweaks and corrections
As well as adapting this to work as a single statement, I have made a number of modifications to your existing code:
NOT IN
forNOT EXISTS
to avoid any issues with null records. IfholdingNo
is nullable, they are equivalent, ifholdingNo
is nullable,NOT EXISTS
is safer - Not Exists Vs Not INYEAR(date_month) = YEAR(@tDate) AND MONTH(date_month) = MONTH(@tDate)
to becomeEOMONTH(@tdate) = EOMONTH(date_month)
. These are syntactically the same, but EOMONTH is Sargable, whereasMONTH
andYEAR
are not.Then a few further links/suggestions that are directly related to changes I have made
EDIT
The above is not correct,
EOMONTH()
is not sargable, so does not perform any better thanYEAR(x) = YEAR(y) AND MONTH(x) = MONTH(y)
, although it is still a bit simpler. If you want a truly sargable predicate you will need to create a start and end date using@tdate
, so you can use:to get the first day of the month for
@tdate
, then almost the same forumla, but add months to 1st February 1900 rather than 1st January to get the start of the next month:So the following:
Will return 1st October and 1st November respectively. Putting this back in your original query would give: