[英]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
检查需要花费很多时间。 如何优化此查询? 对我来说,至少需要在几分钟内执行查询。
确保WHERE
和JOIN
子句谓词是可保留的。 将函数应用于列(例如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_bill
的LEFT 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.