I have following query whose execution time is getting very high with the increase of data. How I can optimize this?
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
try this:
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
Your code have slow performance because you used function and sub queries in where clause, so I made some changes, I hope it works!
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
One thing that stands out is the predicate in this subquery:
(SELECT hlid
FROM tx_bill_pay
WHERE YEAR(date_month) = YEAR(@tdate)
AND hlid IS NOT NULL)
Applying the YEAR
function will prevent an index on that column (assuming one exists) from being used efficiently so a full table scan of tx_bill_pay may be required and will be costly if the table is large. Try refactoring the expression as below so that such an index may be used.
Query optimization involves not just the query but indexes as well so add the DDL to your question as @Larnu requested in the comment.
(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)
The query is basically:
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;
Rewriting the query using JOIN
s is dangerous without understanding the data, because it might introduce duplicates. Instead, I would rewrite using EXISTS
and then introduce indexes:
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;
Then, for the subqueries, you want indexes on:
tx_asset(hlid, id desc, asset)
tx_bill_pay(hlid, date_month, hlid)
tbl_client(id, client_type)
For the main JOIN
, you probably want indexes on:
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
in temp table If date_month
is index column then it matter.It is not SARGAble
.to make this SARGable
do this,
date_month>=@Fromdate and date_month<=@ToDate
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.