如何提高sql server中while循环插入的性能?
这是我的SQL查询。它从临时表中插入了几乎6500多行。但它需要15分钟以上。我该如何改进这一点?谢谢如何提高sql server中while循环插入的性能?,sql,sql-server,performance,tsql,stored-procedures,Sql,Sql Server,Performance,Tsql,Stored Procedures,这是我的SQL查询。它从临时表中插入了几乎6500多行。但它需要15分钟以上。我该如何改进这一点?谢谢 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_
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
正如在评论中所说,循环是完全没有必要的。提高任何循环性能的方法是完全删除它。循环是SQL中的最后手段 据我所知,您的插入可以用一条语句编写:
INSERT tbl_bill(clid, hlid, holdingNo,ClientID, ClientName, billno, date_month, unit, others, fine, due, bill, rebate, remark, payment_date, inserted_by, inserted_date)
SELECT clid = c.id,
hlid = h.id,
h.holdinNo ,
c.cliendID,
clientName = CAST(c.clientName AS NVARCHAR(50)),
BillNo = CONCAT(h.holdinNo, MONTH(@tdate), YEAR(@tdate)),
date_month = @tDate,
unit = 0,
others = CASE WHEN h.hfloor = 0 THEN rs.frate * (h.hfloor - 1) ELSE 0 END,
fine = bs.FineRate * b.Due / 100,
due = b.Due,
bill = @bill, -- This is declared but never assigned
rebate = bs.rebate,
remark = @remark,
payment_date = @pdate,
inserted_by = @userid,
inserted_date = GETDATE()
FROM ( SELECT id, clientdID, ClientName
FROM tbl_client
WHERE status = 1
) AS c
INNER JOIN
( SELECT id, holdinNo, [floor], connect_radius
FROM tx_holding
WHERE status = 1
AND connect_radius <> '0'
AND type = 'Residential'
) AS h
ON c.id = h.clid
LEFT JOIN tbl_radius_setting AS rs
ON rs.radius= CONVERT(real,h.connect_radius)
AND rs.status = 1
AND rs.type = 'Non-Govt.'
LEFT JOIN tbl_bill_setting AS bs
ON bs.Status = 1
LEFT JOIN
( SELECT hlid,
SUM(netbill) AS Due
FROM tbl_bill AS b
WHERE date_month < @tdate
AND (b.ispay = 0 OR b.ispay IS NULL)
GROUP BY hlid
) AS b
ON b.hlid = h.id
WHERE NOT EXISTS
( SELECT 1
FROM tbl_bill AS tb
WHERE EOMONTH(@tdate) = EOMONTH(date_month)
AND tb.holdingNo = h.holdinNo
AND (tb.update_by IS NOT NULL OR tb.ispay=1)
);
要获取@tdate的月份第一天,则几乎与forumla相同,但将月份添加到1900年2月1日,而不是1月1日,以获取下一个月的开始:
DATEADD(MONTH, DATEDIFF(MONTH, '19000201', @tdate), '19000201')
因此,以下是:
DECLARE @Tdate DATE = '2019-10-11';
SELECT DATEADD(MONTH, DATEDIFF(MONTH, '19000101', @tdate), '19000101'),
DATEADD(MONTH, DATEDIFF(MONTH, '19000201', @tdate), '19000201');
将分别于10月1日和11月1日返回。将其放回原始查询将提供:
WHERE NOT EXISTS
( SELECT 1
FROM tbl_bill AS tb
WHERE date_month >= DATEADD(MONTH, DATEDIFF(MONTH, '19000101', @tdate), '19000101'),
AND date_month < DATEADD(MONTH, DATEDIFF(MONTH, '19000201', @tdate), '19000201')
AND tb.holdingNo = h.holdinNo
AND (tb.update_by IS NOT NULL OR tb.ispay=1)
);
正如在评论中所说,循环是完全没有必要的。提高任何循环性能的方法是完全删除它。循环是SQL中的最后手段 据我所知,您的插入可以用一条语句编写:
INSERT tbl_bill(clid, hlid, holdingNo,ClientID, ClientName, billno, date_month, unit, others, fine, due, bill, rebate, remark, payment_date, inserted_by, inserted_date)
SELECT clid = c.id,
hlid = h.id,
h.holdinNo ,
c.cliendID,
clientName = CAST(c.clientName AS NVARCHAR(50)),
BillNo = CONCAT(h.holdinNo, MONTH(@tdate), YEAR(@tdate)),
date_month = @tDate,
unit = 0,
others = CASE WHEN h.hfloor = 0 THEN rs.frate * (h.hfloor - 1) ELSE 0 END,
fine = bs.FineRate * b.Due / 100,
due = b.Due,
bill = @bill, -- This is declared but never assigned
rebate = bs.rebate,
remark = @remark,
payment_date = @pdate,
inserted_by = @userid,
inserted_date = GETDATE()
FROM ( SELECT id, clientdID, ClientName
FROM tbl_client
WHERE status = 1
) AS c
INNER JOIN
( SELECT id, holdinNo, [floor], connect_radius
FROM tx_holding
WHERE status = 1
AND connect_radius <> '0'
AND type = 'Residential'
) AS h
ON c.id = h.clid
LEFT JOIN tbl_radius_setting AS rs
ON rs.radius= CONVERT(real,h.connect_radius)
AND rs.status = 1
AND rs.type = 'Non-Govt.'
LEFT JOIN tbl_bill_setting AS bs
ON bs.Status = 1
LEFT JOIN
( SELECT hlid,
SUM(netbill) AS Due
FROM tbl_bill AS b
WHERE date_month < @tdate
AND (b.ispay = 0 OR b.ispay IS NULL)
GROUP BY hlid
) AS b
ON b.hlid = h.id
WHERE NOT EXISTS
( SELECT 1
FROM tbl_bill AS tb
WHERE EOMONTH(@tdate) = EOMONTH(date_month)
AND tb.holdingNo = h.holdinNo
AND (tb.update_by IS NOT NULL OR tb.ispay=1)
);
要获取@tdate的月份第一天,则几乎与forumla相同,但将月份添加到1900年2月1日,而不是1月1日,以获取下一个月的开始:
DATEADD(MONTH, DATEDIFF(MONTH, '19000201', @tdate), '19000201')
因此,以下是:
DECLARE @Tdate DATE = '2019-10-11';
SELECT DATEADD(MONTH, DATEDIFF(MONTH, '19000101', @tdate), '19000101'),
DATEADD(MONTH, DATEDIFF(MONTH, '19000201', @tdate), '19000201');
将分别于10月1日和11月1日返回。将其放回原始查询将提供:
WHERE NOT EXISTS
( SELECT 1
FROM tbl_bill AS tb
WHERE date_month >= DATEADD(MONTH, DATEDIFF(MONTH, '19000101', @tdate), '19000101'),
AND date_month < DATEADD(MONTH, DATEDIFF(MONTH, '19000201', @tdate), '19000201')
AND tb.holdingNo = h.holdinNo
AND (tb.update_by IS NOT NULL OR tb.ispay=1)
);
首先不使用循环可能是一个开始。SQL不是一种编程语言,而是一种查询语言。它擅长基于集合的操作,而不是迭代操作。您是否尝试过使用单语句insert进行insert。。选择@对不起,你是说CTE?看看执行计划,看看插入临时表的慢部分在哪里让我的眼睛流血。格式太糟糕了。但现在是学习并开始使用ANSI-92样式联接的时候了。他们已经存在超过25年了。但这里最大的问题首先是循环。这应该是一个单insert语句,而不是一行一行地执行。首先不使用循环可能是一个开始。SQL不是一种编程语言,而是一种查询语言。它擅长基于集合的操作,而不是迭代操作。您是否尝试过使用单语句insert进行insert。。选择@对不起,你是说CTE?看看执行计划,看看插入临时表的慢部分在哪里让我的眼睛流血。格式太糟糕了。但现在是学习并开始使用ANSI-92样式联接的时候了。他们已经存在超过25年了。但这里最大的问题首先是循环。这应该是一个单一的insert语句,而不是一行一行地执行。哇。@GarethD非常感谢。我知道这是个糟糕的问题。我需要更多的学习。这个完整的查询不是我写的。不管怎样,我正在检查你的代码。再次感谢你们的辛勤工作和时间。巨大的努力。哇。@GarethD非常感谢。我知道这是个糟糕的问题。我需要更多的学习。这个完整的查询不是我写的。不管怎样,我正在检查你的代码。再次感谢你的辛勤工作和时间。