我正在尝试改进旧的while循环查询。到目前为止,已经尝试过了,但是仍然很慢。不确定SUM的执行速度是否会变慢。
旧查询(超过10分钟)
ALTER PROCEDURE [dbo].[process_tax]
@userid VARCHAR(10),
@remark NVARCHAR(500),
@tdate DATE,
@roadno NVARCHAR(10),
@inst INT
AS
BEGIN
IF OBJECT_ID('tempdb..#tempProcess_tax_1') IS NOT NULL
DROP TABLE #tempProcess_tax_1
CREATE TABLE #tempProcess_tax_1
(
RowID INT IDENTITY(1, 1),
clid_ INT,
hlid_ INT,
holdinNo_ NVARCHAR(500),
holding_ NVARCHAR(50),
clientid_ NVARCHAR(500),
clientName_ NVARCHAR(500)
)
INSERT INTO #tempProcess_tax_1 (clid_, hlid_, holdinNo_, holding_, clientid_, clientName_)
SELECT
cl.clid AS clid_, cl.id AS hlid_, holdinNo, holding,
ClientID AS clientid_, ClientName AS clientName_
FROM
tx_holding AS cl
WHERE
cl.status = 1 AND cl.roadno = @roadno
AND cl.id IN (SELECT hlid FROM tx_asset WHERE asset IS NOT NULL)
AND cl.clid IN (SELECT id FROM tbl_client WHERE client_type = 'Non-Govt.')
AND cl.id NOT IN (SELECT hlid FROM tx_bill_pay
WHERE YEAR(date_month) = YEAR(@tdate)
AND hlid IS NOT NULL
GROUP BY hlid)
DECLARE @NumberRecords_1 INT, @RowCounter_1 INT
SET @NumberRecords_1 = (SELECT COUNT(*) FROM #tempProcess_tax_1)
SET @RowCounter_1 = 1
WHILE @RowCounter_1 <= @NumberRecords_1
BEGIN
DECLARE @clid_ INT
DECLARE @hlid_ INT
DECLARE @holdinNo_ NVARCHAR(50)
DECLARE @holding_ NVARCHAR(50)
DECLARE @clientid_ NVARCHAR(100)
DECLARE @clientName_ NVARCHAR(250)
DECLARE @bill AS MONEY
DECLARE @sr AS MONEY;
SELECT
@clid_ = clid_,
@hlid_ = hlid_,
@holdinNo_ = holdinNo_, @holding_ = holding_,
@clientid_ = clientid_, @clientName_ = clientName_
FROM
#tempProcess_tax_1
WHERE
RowID = @RowCounter_1
SET @bill = (SELECT
CASE WHEN SUM(netvalue) IS NULL
THEN 0
ELSE SUM(netvalue)
END
FROM
tx_bill
WHERE
hlid = @hlid_
AND itemID NOT IN (8, 6)
AND YEAR(date_month) = YEAR(@tdate))
SET @sr = (SELECT
CASE WHEN SUM(asset * rate / 100) IS NULL
THEN 0
ELSE SUM(asset * rate / 100)
END
FROM
tx_bill
WHERE
hlid = @hlid_
AND itemID = 6
AND YEAR(date_month) = YEAR(@tdate))
INSERT INTO tx_bill_pay(clid, hlid, swercharge, pay_bill, pdate, bill_id, holdingNo, holding, ClientID, ClientName, billno, date_month, bill, install, inserted_by, inserted_date)
VALUES (@clid_, @hlid_, @sr, @bill / 4, DATEADD(day, -1, DATEADD(m, 3, @tdate)), CONCAT(@holdinNo_, YEAR(@tdate), '1'), @holdinNo_, @holding_, @clientid_, @clientName_, CONCAT(@holdinNo_, YEAR@tdate)), @tdate, @bill, 1, @userid, GETDATE())
INSERT INTO tx_bill_pay(clid, hlid, swercharge, pay_bill, pdate, bill_id, holdingNo, holding, ClientID, ClientName, billno, date_month, bill, install, inserted_by, inserted_date)
VALUES (@clid_, @hlid_, 0, 2 * (@bill / 4), DATEADD(day, -1, DATEADD(m, 6, @tdate)), CONCAT(@holdinNo_, YEAR(@tdate), '2'), @holdinNo_, @holding_, @clientid_, @clientName_, CONCAT(@holdinNo_, YEAR(@tdate)), @tdate, @bill, 2, @userid, GETDATE())
SET @RowCounter_1 = @RowCounter_1 + 1
END
DROP TABLE #tempProcess_tax_1
END
新查询(1-2分钟)
ALTER PROCEDURE [dbo].[process_tax]
@userid varchar(10),
@remark nvarchar(500),
@tdate date ,
@roadno nvarchar(10),
@inst int
as
BEGIN
insert into tx_bill_pay(
clid,
hlid,
swercharge,
pay_bill,
pdate,
bill_id,
holdingNo,
holding,
ClientID,
ClientName,
billno,
date_month,
bill,
install ,
inserted_by,
inserted_date)
select
cl.clid,
cl.id,
swercharge=(select case when sum(asset*rate/100) is null then 0 else
sum(asset*rate/100) end from tx_bill where hlid=cl.id and
itemID =6 and year(date_month)=YEAR(@tdate)),
pay_bill=(select case when sum(netvalue) is null then 0 else
sum(netvalue) end from tx_bill where hlid=cl.id and itemID not
in(8,6) and year(date_month)=YEAR(@tdate))/4,
DATEADD(day,-1,
DATEADD(m,3,@tdate)),
CONCAT(cl.holdinNo, year(@tdate),'1'),
cl.holdinNo,
cl.holding,
cl.ClientID,
cl.clientName,
CONCAT(cl.holdinNo,
year(@tdate)),
@tdate,
bill=(select case when sum(netvalue) is null then 0 else sum(netvalue)
end from tx_bill where hlid=cl.id and itemID not in(8,6) and
year(date_month)=YEAR(@tdate))/4,
1,
@userid, getdate()
from
(select *
from tx_holding as cl
where cl.status=1 and cl.roadno=@roadno) AS cl
INNER JOIN (
select DISTINCT hlid from tx_asset where asset is not null
) AS A
ON Cl.id = A.hlid
INNER JOIN (
select DISTINCT id from tbl_client where client_type='Non-Govt.'
) AS C
ON cl.clid=C.id
WHERE NOT EXISTS
( SELECT 1
FROM tx_bill_pay as bp
WHERE year(date_month)=year(@tdate)
and bp.hlid=cl.id
)
insert into tx_bill_pay(clid,hlid
,swercharge,pay_bill,pdate,bill_id,holdingNo,holding,ClientID,
ClientName, billno, date_month, bill, install ,inserted_by,
inserted_date)
select
cl.clid,
cl.id,
0,
pay_bill=2*((select case when sum(netvalue) is null then 0 else sum(netvalue) end from tx_bill where hlid=cl.id and itemID not in(8,6) and year(date_month)=YEAR(@tdate))/4),
DATEADD(day,-1,
DATEADD(m,3,@tdate)),
CONCAT(cl.holdinNo, year(@tdate),'2'),
cl.holdinNo,
cl.holding,
cl.ClientID,
cl.clientName,
CONCAT(cl.holdinNo, year(@tdate)) ,
@tdate,
bill=(select case when sum(netvalue) is null then 0 else sum(netvalue)
end from tx_bill where hlid=cl.id and itemID not in(8,6) and year(date_month)=YEAR(@tdate))/4,
2,
@userid, getdate()
from
(select *
from tx_holding as cl
where cl.status=1 and cl.roadno=@roadno) AS cl
INNER JOIN (
select DISTINCT hlid from tx_asset where asset is not null
) AS A
ON Cl.id = A.hlid
INNER JOIN (
select DISTINCT id from tbl_client where client_type='Non-Govt.'
) AS C
ON cl.clid=C.id
WHERE cl.id not in
( SELECT hlid
FROM tx_bill_pay
WHERE year(date_month)=year(@tdate)
and hlid is not null group by hlid
)
答案 0 :(得分:3)
很棒的工作,消除了循环!
我将指出一个可能的性能问题,特别是year(date_month)=year(@tdate)
。
因为列包装在函数中,所以它是non-SARGABLE。这意味着date_month
值不能直接求值,因此,不能使用此列的索引和统计信息。
要解决此问题,我建议进行以下更改:
在SP的顶部定义另外两个变量:
DECLARE @YearStart AS DATETIME, @NextYearStart DATETIME
SET @YearStart = DATEADD(yy, DATEDIFF(yy, 0, @tdate ), 0 )
SET @NextYearStart = DATEADD( yy, @YearStart, 1 )
然后将year(date_month)=year(@tdate)
的所有实例替换为
@YearStart <= date_month AND date_month < @NextYearStart
此表达式用于查找date_month
值,该值大于或等于一年的第一天的午夜(请注意,考虑了时间成分),而小于第二年的午夜。
接下来,我将查看查询计划,看看SQL Server是否给出了"Missing Index" recommendation(如果它确实建议了索引,它应该出现在计划图的正上方)。尝试添加建议的丢失索引,然后检查是否获得性能改进。如果没有改善,请删除索引-有时建议无济于事。
使用带有不同swercharge
条件的pay_bill
表的3个子查询(填充列bill
,tx_bill
,WHERE
)将导致至少3个评估每执行一次主要查询,此表的数量。
根据表的大小,一次计算所有3个子查询并将结果保存到临时表(或表变量)的效率可能更高:
SELECT hlid,
ISNULL( SUM( CASE WHEN itemID NOT IN (8, 6) THEN netvalue END ), 0 ) AS Bill,
ISNULL( SUM( CASE WHEN itemID = 6 THEN asset * rate / 100 END ), 0 ) AS Sr
INTO #Bills
FROM tx_bill
WHERE @YearStart <= date_month AND date_month < @NextYearStart
AND NOT hlid IS NULL
GROUP BY hlid
在主要查询中,如下所示加入该表:
LEFT JOIN #SumBills AS SumBills ON SumBills.hlid = cl.hlid
并在SELECT
中按照以下示例更改分配:
pay_bill= ISNULL( SumBills.Bill, 0 ) / 4,
注意:我相信您在bill
列中有一个错误,因为该值除以4,而该值不在原始光标中。
正如@GarethD在his answer中对您的previous question所说的那样,正确设置代码格式可显着减少理解和更改代码所需的时间。我要进一步说,代码格式代表您对当前问题的态度和理解。
答案 1 :(得分:1)
我添加了非聚集索引,然后我的查询需要5秒钟才能运行,这几乎解决了我的问题。 @Alex感谢您的辛勤工作和时间。我会检查您的提示。我对您的评论进行投票