NOT IN 语句正在减慢我的查询速度
Posted
技术标签:
【中文标题】NOT IN 语句正在减慢我的查询速度【英文标题】:NOT IN statement is slowing down my query 【发布时间】:2019-08-09 07:24:57 【问题描述】:我的查询有问题。我在这里有一个简单的例子来说明我的代码。
SELECT distinct ID
FROM Table
WHERE IteamNumber in (132,434,675) AND Year(DateCreated) = 2019
AND ID NOT IN (
SELECT Distinct ID FROM Table
WHERE IteamNumber in (132,434,675) AND DateCreated < '2019-01-01')
如您所见,我正在检索在 2019 年而非更早时间创建的唯一数据 ID。
select 语句工作正常,但是一旦我使用 NOT IN 语句,查询可能会轻松超过 1 分钟。
我的另一个问题可能与运行 Microsoft Business Central 的 SQL Server 的计算机/服务器性能有关吗?因为即使使用 (NOT IN) 语句,同样的查询也能完美运行,但那是在 Microsoft Dynamics C5 SQL Server 中。
所以我的问题是我的查询有问题还是主要是服务器问题?
更新:这是一个真实的例子:检索 500 行需要 25 秒
Select count(distinct b.No_),'2014'
from [Line] c
inner join [Header] a
on a.CollectionNo = c.CollectionNo
Inner join [Customer] b
on b.No_ = a.CustomerNo
where c.No_ in('2101','2102','2103','2104','2105')
and year(Enrollmentdate)= 2014
and(a.Resignationdate < '1754-01-01 00:00:00.000' OR a.Resignationdate >= '2014-12-31')
and NOT EXISTS(Select distinct x.No_
from [Line] c
inner join [Header] a
on a.CollectionNo = c.CollectionNo
Inner join [Customer] x
on x.No_ = a.CustomerNo
where x.No_ = b.No_ and
c.No_ in('2101','2102','2103','2104','2105')
and Enrollmentdate < '2014-01-01'
and(a.Resignationdate < '1754-01-01 00:00:00.000' OR a.Resignationdate > '2014-12-31'))
【问题讨论】:
如果不了解更多关于您的数据库的信息,实际上无法回答这个问题,例如多少行,什么索引等。要获得实际的性能帮助,您确实需要向我们展示您的执行计划。 关注这个话题:NOT IN vs NOT EXISTS 在您的第一个查询中,您只包括那些日期在 2019 年创建的 ID。它将自动不包括任何具有“DateCreated 将Year(DateCreated) = 2019
替换为DateCreated >= '2019-01-01'
并放弃NOT IN
。
@Ritika,如果 ID 不是唯一的,这是不正确的:It will automatically not include any of the ID that have 'DateCreated < '2019-01-01
【参考方案1】:
如果我理解正确,您可以将查询编写为带有HAVING
子句的GROUP BY
查询:
SELECT ID
FROM t
WHERE IteamNumber in (132, 434, 675)
GROUP BY ID
HAVING MIN(DateCreated) >= '20190101' -- no row earlier than 2019
AND MIN(DateCreated) < '20200101' -- at least one row less than 2020
这将删除存在较早记录的行。您可以通过创建覆盖索引来进一步提高性能:
CREATE INDEX IX_t_0001 ON t (ID) INCLUDE (IteamNumber, DateCreated)
【讨论】:
仅删除 OP 表上的第二次扫描可能会有很大的好处。您可能还需要在HAVING
中添加 MAX
子句,以确保不会捕获 2020 年的行(即使只是未来校对的一种形式),或者以便 OP 可以看到如何将其应用于不同年份。
嗨,萨尔曼,我刚刚尝试了你的灵魂,但我没有检索到任何数据。我使用 NOT in 的原因,因为 customerID 可能在 3 年前取消了他们的订阅,然后他们可以选择在 2019 年再次订阅,使用相同的帐号,即 id(在本例中)
另外,使用索引或强制索引存储过程而不让 sql server 选择它不是一个坏主意吗?
@MishMish 此查询应生成与您的查询相同的结果。如果它没有给你想要的结果,你可以在生产代码中使用不同的 where 子句。另外,我只是建议和索引,如果 SQL Server 发现它有用,它会使用它。没有强迫。
嗨,萨尔曼,我在上面添加了原始查询,不确定它是否适用于有声明【参考方案2】:
我通常更喜欢 JOINs 而不是 INs,你可以获得相同的结果,但引擎往往能够更好地优化它。
您将主查询 (T1) 与 IN 子查询 (T2) 连接起来,然后过滤 T2.ID 为空,确保您没有找到任何符合这些条件的记录。
SELECT distinct T1.ID
FROM Table T1
LEFT JOIN Table T2 on T2.ID = T1.ID AND
T2.IteamNumber in (132,434,675) AND T2.DateCreated < '2019-01-01'
WHERE T1.IteamNumber in (132,434,675) AND Year(T1.DateCreated) = 2019 AND
T2.ID is null
更新:这是根据您的真实查询更新的提案。由于您的子查询具有内部连接,因此我创建了一个 CTE,因此您可以离开连接该子查询。功能是相同的,您将主查询与子查询连接起来,并且只返回在子查询中没有找到匹配记录的行。
with previous as (
Select x.No_
from [Line] c
inner join [Header] a on a.CollectionNo = c.CollectionNo
inner join [Customer] x on x.No_ = a.CustomerNo
where c.No_ in ('2101','2102','2103','2104','2105')
and Enrollmentdate < '2014-01-01'
and (a.Resignationdate < '1754-01-01 00:00:00.000' OR a.Resignationdate > '2014-12-31'))
)
Select count(distinct b.No_),'2014'
from [Line] c
inner join [Header] a on a.CollectionNo = c.CollectionNo
inner join [Customer] b on b.No_ = a.CustomerNo
left join previous p on p.No_ = b.No_
where c.No_ in ('2101','2102','2103','2104','2105')
and year(Enrollmentdate)= 2014
and (a.Resignationdate < '1754-01-01 00:00:00.000' OR a.Resignationdate >= '2014-12-31')
and p.No_ is null
【讨论】:
嘿,马克,我一定要试试你的例子 还有一个问题,你知道 in 语句中的整数是否比 nvarchar/varchar 值表现更好? @MishMish,如果该字段被索引,我认为它是整数还是字符串并不重要,但如果它没有被索引,那么是的,引擎会比字符串更快地比较整数. 为字段做索引简单吗?它在存储过程中,我认为将索引实现到存储过程中是不好的 我刚刚用实际代码编辑了我的问题,你能告诉我如何使用多个连接来完成【参考方案3】:问题是因为您的IN
语句,我认为最好避免使用任何IN
语句而不是这个,使用子查询创建join
并使用where
子句过滤掉您的数据。
在IN
语句的情况下,表的每条记录都映射到子查询的所有记录,这肯定会减慢您的处理速度。
如果必须使用IN
子句,则将其与index
一起使用。为您尊重的列创建适当的索引,从而提高您的性能。
您可以使用EXISTS
代替IN
来提高查询的性能。
EXISTS
的示例是:
SELECT distinct ID
FROM Table AS T
WHERE IteamNumber in (132,434,675) AND Year(DateCreated) = 2019
AND NOT EXISTS (
SELECT Distinct ID FROM Table AS T2
WHERE T1.ID=T2.ID
AND IteamNumber in (132,434,675) AND DateCreated < '2019-01-01' )
【讨论】:
一个设计良好的数据库应该能够处理in
语句。与任何数据库性能一样,我们需要了解全局以提供有用的建议。
谢谢你的darkrob,你能用我的例子在代码中演示一下你将如何使用join或exists吗?
我觉得奇怪的是它无法处理 Not in 语句,我们谈论的记录并不多,基本上我们在这里谈论的 Dynamics 业务中心
" 在我看来,最好避免任何 IN 语句,而不是 this" 是一个非常全面的语句。你是说用JOIN (VALUES(1),(2)) V(I) ON T.ID = V.I
代替WHERE T.ID IN (1,2)
吗?在许多情况下,IN
可以与JOIN
一样高效,而IN
和JOIN
不是同义词; JOIN
将返回可能创建重复的所有相关行,而 IN
或 EXISTS
(我倾向于使用)不会。它们可以在功能上相似,但并不相同。以上是关于NOT IN 语句正在减慢我的查询速度的主要内容,如果未能解决你的问题,请参考以下文章