简体   繁体   English

SQL Server:查询优化

[英]SQL Server: query optimization

I have the following query which takes around 4 minutes to execute. 我有以下查询,大约需要4分钟才能执行。

DECLARE @tdate DATETIME = '2019-09-01 00:00:00.000'

SELECT c.id AS clid,
       h.id AS hlid,
       h.holdinNo,
       c.cliendID,
       c.clientName,
       h.floor,
       h.connect_radius
FROM   [db_land].[dbo].tbl_client AS c
       INNER JOIN [db_land].[dbo].tx_holding AS h
               ON c.id = h.clid
WHERE  h.status = 1
       AND h.connect_radius IS NOT NULL
       AND c.status = 1
       AND h.type = 'Residential'
       AND h.holdinNo NOT IN (SELECT holdingNo
                              FROM   [db_land].[dbo].tbl_bill
                              WHERE  year(date_month) = YEAR(@tdate)
                                     AND MONTH(date_month) = MONTH(@tdate)
                                     AND ( update_by IS NOT NULL
                                            OR ispay = 1 )) 

I found the inner join takes only few seconds. 我发现内部联接只需要几秒钟。

SELECT c.id AS clid,
       h.id AS hlid,
       h.holdinNo,
       c.cliendID,
       c.clientName,
       h.floor,
       h.connect_radius
FROM   [db_land].[dbo].tbl_client AS c
       INNER JOIN [db_land].[dbo].tx_holding AS h
               ON c.id = h.clid
WHERE  h.status = 1
       AND h.connect_radius IS NOT NULL
       AND c.status = 1
       AND h.type = 'Residential' 

It's the NOT IN checking which takes a lot of time. NOT IN检查需要花费很多时间。 How I can optimize this query? 如何优化此查询? For me it's needed to execute the query at least with in minute. 对我来说,至少需要在几分钟内执行查询。

Make sure the WHERE and JOIN clause predicates are sargable. 确保WHEREJOIN子句谓词是可保留的。 Applying a function to a column (eg YEAR(date_month) ) prevents indexes on the column from being used efficiently. 将函数应用于列(例如YEAR(date_month) )会阻止有效使用列上的索引。

Try this expression instead to avoid the functions. 请尝试使用此表达式来避免使用函数。 There are other methods depending on the SQL Server version. 还有其他方法取决于SQL Server版本。

WHERE
    date_month >= DATEADD(day, 1, DATEADD(month, -1, EOMONTH(@tdate)))
    AND date_month < DATEADD(day, 1, DATEADD(month, 1, EOMONTH(@tdate)))

Try by replacing NOT IN with a LEFT JOIN of the table [db_land].[dbo].tbl_bill on all the conditions and adding in the WHERE clause holdingNo is null so the returned rows are the non matching rows: 在所有条件下,尝试用表[db_land].[dbo].tbl_billLEFT JOIN替换NOT IN ,并在WHERE子句中添加[db_land].[dbo].tbl_bill holdingNo is null以便返回的行是不匹配的行:

select c.id as clid, h.id as hlid,h.holdinNo, c.cliendID, c.clientName, h.floor, h.connect_radius 
from [db_land].[dbo].tbl_client as c 
inner join [db_land].[dbo].tx_holding as h 
on c.id= h.clid
left join [db_land].[dbo].tbl_bill as b 
on b.holdingNo = h.holdinNo and year(b.date_month) = YEAR(@tdate) and MONTH(b.date_month) = MONTH(@tdate) 
and (b.update_by is not null or b.ispay = 1)
where h.status = 1 and h.connect_radius is not null and c.status=1 and h.type='Residential' and b.holdingNo is null

I would recommend changing the NOT IN to NOT EXISTS and adding an index: 我建议将NOT IN更改为NOT EXISTS并添加索引:

WHERE . . . AND
      NOT EXISTS (SELECT 1
                  FROM [db_land].[dbo].tbl_bill b
                  WHERE b.holdingNo = h.holdingNo AND
                        b.date_month >= DATEFROMPARTS(YEAR(@tdate), MONTH(@tdate), 1) AND
                        b.date_month < DATEADD(month, 1, DATEFROMPARTS(YEAR(@tdate), MONTH(@tdate), 1)) AND
                        (b.update_by IS NOT NULL OR b.ispay = 1
                 )

Then the index that you want is on tbl_bill(holdingNo, date_month, update_by, ispay) . 然后,您想要的索引在tbl_bill(holdingNo, date_month, update_by, ispay)

Put your sub query into temp table : 将您的子查询放入临时表:

DECLARE @tdate DATETIME = '2019-09-01 00:00:00.000'

SELECT holdingNo 
into #TmpholdingNo
FROM   [db_land].[dbo].tbl_bill
WHERE  year(date_month) = YEAR(@tdate)
        AND MONTH(date_month) = MONTH(@tdate)
        AND ( update_by IS NOT NULL
        OR ispay = 1 )

SELECT c.id AS clid,
       h.id AS hlid,
       h.holdinNo,
       c.cliendID,
       c.clientName,
       h.floor,
       h.connect_radius
FROM   [db_land].[dbo].tbl_client AS c
       INNER JOIN [db_land].[dbo].tx_holding AS h
               ON c.id = h.clid
WHERE  h.status = 1
       AND h.connect_radius IS NOT NULL
       AND c.status = 1
       AND h.type = 'Residential'
       AND h.holdinNo NOT IN (SELECT holdingNo from #TmpholdingNo) 

drop table #TmpholdingNo

put the filter list at variable,then them apply the filter 将过滤器列表放在变量上,然后应用过滤器

     DECLARE @filter TABLE  INSERT INTO @filter SELECT  FROM  [db_land].[dbo].tbl_bill 

them apply the filter 他们应用过滤器

DECLARE @tdate DATETIME = '2019-09-01 00:00:00.000'

SELECT c.id AS clid,
       h.id AS hlid,
       h.holdinNo,
       c.cliendID,
       c.clientName,
       h.floor,
       h.connect_radius
FROM [db_land].[dbo].tbl_client AS c
INNER JOIN [db_land].[dbo].tx_holding AS h ON c.id= h.clid
WHERE h.status=1
  AND h.connect_radius IS NOT NULL
  AND c.status=1
  AND h.type='Residential'
  AND h.holdinNo NOT IN (filter)

Rather than using functions in your WHERE clause try calculating the start and end filter dates, using OPTION (RECOMPILE) can help SQL to use the actual values of your variables in your query plan. 使用OPTION (RECOMPILE)可以帮助SQL在查询计划中使用变量的实际值,而不是在WHERE子句中使用函数来尝试计算开始和结束过滤器日期。 I would also change NOT IN to NOT EXISTS : 我也将NOT IN更改为NOT EXISTS

DECLARE @tdate DATETIME = '2019-09-01 00:00:00.000'
DECLARE @startDate DATE = DATEFROMPARTS(YEAR(@tdate), MONTH(@tdate), 1)
DECLARE @endDate DATE = DATEADD(day,1,EOMONTH(@tdate))


SELECT c.id AS clid,
       h.id AS hlid,
       h.holdinNo,
       c.cliendID,
       c.clientName,
       h.floor,
       h.connect_radius
FROM   [db_land].[dbo].tbl_client AS c
       INNER JOIN [db_land].[dbo].tx_holding AS h
               ON c.id = h.clid
WHERE  h.status = 1
       AND h.connect_radius IS NOT NULL
       AND c.status = 1
       AND h.type = 'Residential'
       AND NOT EXISTS (SELECT holdingNo
                       FROM   [db_land].[dbo].tbl_bill
                       WHERE  holdingNo = h.holdinNo AND
                       date_month >= @startDate AND
                       date_month < @endDate AND 
                                     AND ( update_by IS NOT NULL
                                            OR ispay = 1 )) 
OPTION (RECOMPILE)

give a try try this: 试试看试试看:

select main.* from 

    (SELECT c.id AS clid,
           h.id AS hlid,
           h.holdinNo,
           c.cliendID,
           c.clientName,
           h.floor,
           h.connect_radius
    FROM   [db_land].[dbo].tbl_client AS c
           INNER JOIN [db_land].[dbo].tx_holding AS h
                   ON c.id = h.clid
    WHERE  h.status = 1
           AND h.connect_radius IS NOT NULL
           AND c.status = 1
           AND h.type = 'Residential')main

left join 

    (select holdingNo from 
        (SELECT holdingNo, update_by, ispay
        FROM   [db_land].[dbo].tbl_bill
        WHERE  year(date_month) = YEAR(@tdate)
        AND MONTH(date_month) = MONTH(@tdate))bill1
    where update_by IS NOT NULL OR ispay = 1)bill2

on main.holdinNo = bill2.holdinNo


where bill2.holdinNo is null

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM