[英]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.