[英]SQL Server: query performance optimization
我有以下查询,其执行时间随着数据的增加而变得非常高。 我该如何优化呢?
SELECT
txh.clid AS clid,
txh.id AS hlid,
holdinNo,
holding,
ClientID AS cliendID,
ClientName,
itm.itemID,
itm.Item,
itm.rate,
(SELECT TOP 1 asset
FROM tx_asset
WHERE tx_asset.hlid = txh.id
ORDER BY id DESC) AS asset
FROM
tx_holding AS txh
INNER JOIN
tx_set_bill_holding AS itm ON txh.id = itm.hlid AND itm.status = 1
WHERE
txh.id IN (SELECT hlid FROM tx_asset
WHERE asset IS NOT NULL AND asset != 0)
AND txh.id NOT IN (SELECT hlid FROM tx_bill_pay
WHERE YEAR(date_month) = YEAR(@tdate)
AND hlid IS NOT NULL)
AND txh.clid IN (SELECT id FROM tbl_client
WHERE client_type = 'Non-Govt.')
AND itm.type = 'Non-Govt.'
AND txh.roadno = @roadno
尝试这个:
SELECT hlid,max(asset) asset into #temp FROM tx_asset group by hlid
SELECT txh.clid AS clid,
txh.id AS hlid,
holdinNo,
holding,
ClientID AS cliendID,
ClientName,
itm.itemID,
itm.Item,
itm.rate,
t.asset
FROM tx_holding AS txh
INNER JOIN tx_set_bill_holding AS itm ON txh.id = itm.hlid AND itm.status=1
INNER /*or left*/ join #temp t on t.hlid=txh.id
WHERE txh.id IN
(
SELECT hlid FROM tx_asset WHERE asset IS NOT NULL AND asset!=0
)
AND txh.id NOT IN
(
SELECT hlid FROM tx_bill_pay WHERE year(date_month)=year(@tdate) AND hlid IS NOT NULL
)
AND txh.clid IN
(
SELECT id FROM tbl_client WHERE client_type='Non-Govt.'
)
AND itm.type='Non-Govt.' AND txh.roadno=@roadno
Drop table #temp
您的代码性能很慢,因为您在 where 子句中使用了 function 和子查询,所以我做了一些更改,希望它有效!
declare @tdate_year dateTime
select @tdate_year = year(@tdate)
WITH
tx_asset AS
(
SELECT ROW_NUMBER () OVER (ORDER BY asset) rank
FROM tx_asset
),
tbp AS
(
SELECT *,year(tx_bill_pay.date_month) year_date
FROM tx_bill_pay
),
txh AS
(
SELECT *
FROM tx_holding AS a
INNER JOIN tx_asset As b
ON a.id=b.hlid
LEFT JOIN tbp As c
ON a.id=c.id and year_date=@tdate_year
INNER JOIN tbl_client As d on a.id=d.id
WHERE
b.rank = 1 AND b.asset IS NOT NULL AND b.asset!=0 AND
c.id IS NULL AND c.hlid IS NOT NULL AND
d.client_type='Non-Govt.'
)
SELECT
txh.clid AS clid,
txh.id AS hlid,
holdinNo,
holding,
ClientID AS cliendID,
ClientName,
itm.itemID,
itm.Item,
itm.rate,
asset
FROM txh
INNER JOIN tx_asset AS x
ON txh.id = x.hlid
INNER JOIN tx_set_bill_holding AS itm
ON txh.id = itm.hlid AND itm.status=1
WHERE
itm.type='Non-Govt.' AND
txh.roadno=@roadno
突出的一件事是这个子查询中的谓词:
(SELECT hlid
FROM tx_bill_pay
WHERE YEAR(date_month) = YEAR(@tdate)
AND hlid IS NOT NULL)
应用YEAR
function 将阻止该列上的索引(假设存在)被有效使用,因此可能需要对 tx_bill_pay 进行全表扫描,如果表很大,则成本会很高。 尝试如下重构表达式,以便可以使用这样的索引。
查询优化不仅涉及查询,还涉及索引,因此请按照@Larnu 在评论中的要求将 DDL 添加到您的问题中。
(SELECT hlid
FROM tx_bill_pay
WHERE date_month >= DATEADD(year, DATEDIFF(year, '', @tdate))
AND date_month < DATEADD(year, DATEDIFF(year, '', @tdate))
AND hlid IS NOT NULL)
查询基本上是:
SELECT . . .,
(SELECT TOP 1 asset
FROM tx_asset
WHERE tx_asset.hlid = txh.id
ORDER BY id DESC) AS asset
FROM tx_holding txh INNER JOIN
tx_set_bill_holding itm
ON txh.id = itm.hlid AND itm.status = 1
WHERE txh.id IN (SELECT a.hlid
FROM tx_asset a
WHERE a.asset IS NOT NULL AND a.asset <> 0
) AND
txh.id NOT IN (SELECT bp.hlid
FROM tx_bill_pay bp
WHERE YEAR(bp.date_month) = YEAR(@tdate) AND
bp.hlid IS NOT NULL
) AND
txh.clid IN (SELECT c.id
FROM tbl_client c
WHERE c.client_type = 'Non-Govt.'
) AND
itm.type = 'Non-Govt.' AND
txh.roadno = @roadno;
在不了解数据的情况下使用JOIN
重写查询是危险的,因为它可能会引入重复。 相反,我会使用EXISTS
重写,然后引入索引:
SELECT . . .,
(SELECT TOP 1 a.asset
FROM tx_asset a
WHERE a.hlid = txh.id
ORDER BY id DESC) AS asset
FROM tx_holding txh INNER JOIN
tx_set_bill_holding itm
ON txh.id = itm.hlid AND itm.status = 1
WHERE EXISTS (SELECT 1
FROM tx_asset a
WHERE a.hlid = txh.id AND
a.asset <> 0 -- checks for NULL as well
) AND
NOT EXISTS (SELECT 1
FROM tx_bill_pay bp
WHERE bp.hlid = txh.id AND
YEAR(bp.date_month) = YEAR(@tdate) AND
bp.hlid IS NOT NULL
) AND
EXISTS (SELECT 1
FROM tbl_client c
WHERE c.id = txh.clid AND
c.client_type = 'Non-Govt.'
) AND
itm.type = 'Non-Govt.' AND
txh.roadno = @roadno;
然后,对于子查询,您需要以下索引:
tx_asset(hlid, id desc, asset)
tx_bill_pay(hlid, date_month, hlid)
tbl_client(id, client_type)
对于主JOIN
,您可能需要以下索引:
tx_holding(roadno, id)
tx_set_bill_holding(hlid, status, type)
Declare @Fromdate datetime='2019-01-01'
Declare @ToDate datetime='2019-12-31'
create table #tx_asset(hlid int,asset int)
insert into #tx_asset(hlid,asset)
SELECT hlid,asset FROM tx_asset
WHERE asset > 0 -- this wil work for null and <>0 both
SELECT
txh.clid AS clid,
txh.id AS hlid,
holdinNo,
holding,
ClientID AS cliendID,
ClientName,
itm.itemID,
itm.Item,
itm.rate,
oa.asset
FROM
dbo.tx_holding AS txh
INNER JOIN
dbo.tx_set_bill_holding AS itm ON txh.id = itm.hlid AND itm.status = 1
outer apply(SELECT TOP 1 asset
FROM #tx_asset
WHERE tx_asset.hlid = txh.id
ORDER BY id DESC)oa
WHERE
exists (SELECT 1 FROM #tx_asset
WHERE hlid=txh.id )
AND NOT exists (SELECT hlid FROM dbo.tx_bill_pay
WHERE hlid=txh.id and YEAR(date_month) = YEAR(@tdate)
AND hlid IS NOT NULL)
AND exists IN (SELECT 1 FROM dbo.tbl_client
WHERE id=txh.clid and client_type = 'Non-Govt.')
AND itm.type = 'Non-Govt.'
AND txh.roadno = @roadno
tx_asset
放入临时表 如果date_month
是索引列,那么它很重要。这不是SARGAble
。让这个SARGable
做到这一点,
date_month>=@Fromdate 和 date_month<=@ToDate
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.