SQL Server: оптимизация производительности запросов - PullRequest
0 голосов
/ 21 сентября 2019

У меня следующий запрос, время выполнения которого очень увеличивается с увеличением данных.Как я могу оптимизировать это?

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

Ответы [ 5 ]

0 голосов
/ 23 сентября 2019
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. Вместо подзапроса используйте Outer Apply.
  3. Если date_month является столбцом индекса, тогда это имеет значение.Это не SARGAble. Чтобы сделать это SARGable сделать это,

    date_month> = @ Fromdate и date_month <= @ ToDate </p>

0 голосов
/ 21 сентября 2019

Запрос в основном:

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 s, опасно без понимания данных, поскольку он может содержать дубликаты.Вместо этого я бы переписал с использованием 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)
0 голосов
/ 21 сентября 2019

Ваш код имеет низкую производительность, потому что вы использовали функцию и подзапросы в предложении where, поэтому я внес некоторые изменения, надеюсь, это сработает!

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
0 голосов
/ 21 сентября 2019

Одна вещь, которая выделяется, это предикат в этом подзапросе:

(SELECT hlid 
 FROM tx_bill_pay 
 WHERE YEAR(date_month) = YEAR(@tdate) 
       AND hlid IS NOT NULL)

Применение функции YEAR не позволит эффективно использовать индекс в этом столбце (при условии, что он существует), поэтому полная таблицаСканирование tx_bill_pay может потребоваться и будет дорогостоящим, если таблица большая.Попробуйте выполнить рефакторинг выражения, как показано ниже, чтобы можно было использовать такой индекс.

Оптимизация запроса включает в себя не только запрос, но и индексы, поэтому добавьте DDL к вашему вопросу, как @Larnu, запрошенное в комментарии.

(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)
0 голосов
/ 21 сентября 2019

попробуйте это:

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
...