繁体   English   中英

SQL Server:查询优化

[英]SQL Server: query optimization

我有以下查询,大约需要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 )) 

我发现内部联接只需要几秒钟。

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' 

NOT IN检查需要花费很多时间。 如何优化此查询? 对我来说,至少需要在几分钟内执行查询。

确保WHEREJOIN子句谓词是可保留的。 将函数应用于列(例如YEAR(date_month) )会阻止有效使用列上的索引。

请尝试使用此表达式来避免使用函数。 还有其他方法取决于SQL Server版本。

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

在所有条件下,尝试用表[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

我建议将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
                 )

然后,您想要的索引在tbl_bill(holdingNo, date_month, update_by, ispay)

将您的子查询放入临时表:

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

将过滤器列表放在变量上,然后应用过滤器

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

他们应用过滤器

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)

使用OPTION (RECOMPILE)可以帮助SQL在查询计划中使用变量的实际值,而不是在WHERE子句中使用函数来尝试计算开始和结束过滤器日期。 我也将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)

试试看试试看:

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