Sql 为什么这个查询的运行时间比子查询的总和要长得多?

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) 我还应该在这张便笺中包括什么来为您提供足够的信息以提供帮助 (实际

像下面这样的查询如何运行超过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)
我还应该在这张便笺中包括什么来为您提供足够的信息以提供帮助

(实际的查询如下,以防引起问题的查询有一些棘手和具体的地方。)


您可能没有意识到这一点,但查询引擎会将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
条件,甚至不会构建所有条件。这就是所谓的半连接,有各种各样的算法来执行它。在我发布这篇文章之前,我翻阅了执行计划(我真的不知道如何使用),找到了“左反半连接”在我的询问中,我发现这是我需要研究的东西,但要做的事情太多了,以至于我很难抽出时间阅读我想读的所有东西