[英]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. 确保WHERE
和JOIN
子句谓词是可保留的。 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_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
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.