繁体   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