Sql 为什么这个查询的运行时间比子查询的总和要长得多?
像下面这样的查询如何运行超过16个小时?(我们停止执行以研究优化,但我们都不是DB专家。)似乎执行基于集合的排除应该非常简单,对吗Sql 为什么这个查询的运行时间比子查询的总和要长得多?,sql,sql-server,Sql,Sql Server,像下面这样的查询如何运行超过16个小时?(我们停止执行以研究优化,但我们都不是DB专家。)似乎执行基于集合的排除应该非常简单,对吗 SELECT field FROM (subquery that returns 1173126 rows in 20 seconds) WHERE field NOT IN (subquery that returns 3927646 rows in 69 seconds) 我还应该在这张便笺中包括什么来为您提供足够的信息以提供帮助 (实际
SELECT
field
FROM
(subquery that returns 1173126 rows in 20 seconds)
WHERE
field NOT IN (subquery that returns 3927646 rows in 69 seconds)
我还应该在这张便笺中包括什么来为您提供足够的信息以提供帮助
(实际的查询如下,以防引起问题的查询有一些棘手和具体的地方。)
您可能没有意识到这一点,但查询引擎会将not IN语句转换为IF语句。因此,在您的示例中,它正在构建一个包含所有这些行(3.9M)的巨型IF语句。然后,它必须评估每个IF条件,以查看该值是否存在。毫不奇怪,它需要16个多小时才能运行
您最好设法将其转换为EXISTS或join。您可能没有意识到这一点,但not IN语句会被查询引擎转换为IF语句。因此,在您的示例中,它正在构建一个包含所有这些行(3.9M)的巨型IF语句。然后,它必须评估每个IF条件,以查看该值是否存在。毫不奇怪,它需要16个多小时才能运行
您最好设法将其转换为EXISTS或join。第二个子查询将针对第一个子查询中的每一行运行一次 也就是说,预计完成时间约为(1173126*69)=80945394秒 大约是154年
添加实际查询后,最好的方法是通过向表中添加索引来优化这两个查询。我不能确切地告诉您要添加哪些索引,但是有很多关于为表选择正确索引的好文章 第二个子查询将针对第一个子查询中的每一行运行一次 也就是说,预计完成时间约为(1173126*69)=80945394秒 大约是154年
添加实际查询后,最好的方法是通过向表中添加索引来优化这两个查询。我不能确切地告诉您要添加哪些索引,但是有很多关于为表选择正确索引的好文章 您似乎正在搜索在过去一年内但不是在前5年内购买的地址
SELECT DISTINCT a.line1, SUBSTRING(a.zip, 1, 5)
FROM addresses a
WHERE id IN
(
SELECT c.addressId
FROM customers c
JOIN registrations r
ON r.custId = c.id
AND r.purchaseDate > DATEADD(year, -1 ,getdate())
)
AND NOT EXISTS
(
SELECT NULL
FROM customers c
JOIN registrations r
ON r.custId = c.id
JOIN addresses ai
ON ai.id = c.addressId
WHERE r.purchaseDate BETWEEN DATEADD(year,-5,getdate()) AND DATEADD(year,-1,getdate())
AND ai.line1 = a.line1
AND SUBSTRING(ai.zip, 1, 5) = SUBSTRING(a.zip, 1, 5)
)
此查询关注具有不同ID的地址上的
line1,zip
的重复项。你有这样的副本吗?你似乎在搜索在过去一年内购买过但在过去5年内没有购买过的地址
SELECT DISTINCT a.line1, SUBSTRING(a.zip, 1, 5)
FROM addresses a
WHERE id IN
(
SELECT c.addressId
FROM customers c
JOIN registrations r
ON r.custId = c.id
AND r.purchaseDate > DATEADD(year, -1 ,getdate())
)
AND NOT EXISTS
(
SELECT NULL
FROM customers c
JOIN registrations r
ON r.custId = c.id
JOIN addresses ai
ON ai.id = c.addressId
WHERE r.purchaseDate BETWEEN DATEADD(year,-5,getdate()) AND DATEADD(year,-1,getdate())
AND ai.line1 = a.line1
AND SUBSTRING(ai.zip, 1, 5) = SUBSTRING(a.zip, 1, 5)
)
此查询关注具有不同ID的地址上的
line1,zip
的重复项。你有这样的重复吗?是的,我们正在寻找“新”客户,id上有重复的line1、zip组合。@clweeks:因此,如果3年前从同一地址购买了3
,但id不同,则不算是新的,对吗?正确。家庭中的其他人可能进行了购买(或者他们的数据键入不正确),我们正在寻找进行了新购买的地址/家庭(“新”包括五年前购买的人,但从那以后没有)。是的,它在19秒内运行,返回852238行。我的队友正在阅读EXISTS(我们还没有使用它!)。顺便说一句,非常感谢你的帮助@clweeks:给他这个链接阅读(它解决了这个问题):是的,我们正在寻找“新”客户,id上有重复的line1、zip组合。@clweeks:所以如果3年前从同一地址购买了3
,但id不同,它不算是新的,对吗?正确。家庭中的其他人可能进行了购买(或者他们的数据键入不正确),我们正在寻找进行了新购买的地址/家庭(“新”包括五年前购买的人,但从那以后没有)。是的,它在19秒内运行,返回852238行。我的队友正在阅读EXISTS(我们还没有使用它!)。顺便说一句,非常感谢你的帮助@clweeks:给他这个链接阅读(它解决了这个问题):这不会评估每个IF
条件,甚至不会构建所有条件。这就是所谓的半连接,有各种各样的算法来执行它。在我发布这篇文章之前,我翻阅了执行计划(我真的不知道如何使用),找到了“左反半连接”在我的询问中,我发现这是我需要研究的东西,但要做的事情太多了,以至于我很难抽出时间来阅读我想读的所有东西。@Quassnoi-谢谢你的更正。我的理解是创建了IF语句。如果行数很小,然后对行数较大的行执行半联接,是否会执行此操作?这实际上是反联接(不在
),而不是半联接(在
)。反连接是不在操作中的的同义词。与普通联接一样,有不同的算法来执行它:嵌套循环反联接
(您的if条件在第一次匹配时停止)、合并反联接
,哈希左半联接
,哈希右半联接
;采用不同的归约算法,可并行化;所有这些都具有顺序访问的优化,这些优化可能适用,也可能不适用。具体发生的情况仅显示在计划中。这不会评估每个IF
条件,甚至不会构建所有条件。这就是所谓的半连接,有各种各样的算法来执行它。在我发布这篇文章之前,我翻阅了执行计划(我真的不知道如何使用),找到了“左反半连接”在我的询问中,我发现这是我需要研究的东西,但要做的事情太多了,以至于我很难抽出时间阅读我想读的所有东西