簡體   English   中英

SQL 服務器:查詢性能優化

[英]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
  1. tx_asset放入臨時表
  2. 而不是子查詢使用外部應用。
  3. 如果date_month是索引列,那么它很重要。這不是SARGAble 。讓這個SARGable做到這一點,

    date_month>=@Fromdate 和 date_month<=@ToDate

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM