Naz*_*ain 1 sql t-sql sql-server performance 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_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
Run Code Online (Sandbox Code Playgroud)
正如评论中所说,循环是完全不必要的。改善任何循环性能的方法是完全将其删除。循环是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)
);
Run Code Online (Sandbox Code Playgroud)
请花一点点盐,尝试将逻辑拼凑起来是非常艰巨的工作,因此可能需要一些细微的调整和更正
除了将其修改为单个语句之外,我还对您现有的代码进行了许多修改:
NOT IN为NOT EXISTS避免与空记录任何问题。如果holdingNo为null,则等效;如果holdingNo为null,则更NOT EXISTS安全- 不存在与不存在YEAR(date_month) = YEAR(@tDate) AND MONTH(date_month) = MONTH(@tDate)变为EOMONTH(@tdate) = EOMONTH(date_month)。这些在语法上是相同的,但是EOMONTH是Sargable,而MONTH和YEAR不是。然后是与我所做的更改直接相关的其他一些链接/建议
编辑
上面的内容是不正确的,EOMONTH()不是可固定的,因此YEAR(x) = YEAR(y) AND MONTH(x) = MONTH(y)虽然仍然简单一些,但它的性能也不比更好。如果您想要一个真正可预测的谓词,则需要使用来创建开始日期和结束日期@tdate,因此可以使用:
DATEADD(MONTH, DATEDIFF(MONTH, '19000101', @tdate), '19000101')
Run Code Online (Sandbox Code Playgroud)
来获取的月份的第一天@tdate,然后是几乎相同的论坛,但要在1900年2月1日之前(而不是1月1日)添加月份,以获取下个月的开始时间:
DATEADD(MONTH, DATEDIFF(MONTH, '19000201', @tdate), '19000201')
Run Code Online (Sandbox Code Playgroud)
因此,以下内容:
DECLARE @Tdate DATE = '2019-10-11';
SELECT DATEADD(MONTH, DATEDIFF(MONTH, '19000101', @tdate), '19000101'),
DATEADD(MONTH, DATEDIFF(MONTH, '19000201', @tdate), '19000201');
Run Code Online (Sandbox Code Playgroud)
分别返回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)
);
Run Code Online (Sandbox Code Playgroud)