Sql server 如何强制子查询与临时表一样执行?

Sql server 如何强制子查询与临时表一样执行?,sql-server,performance,sql-server-2008,optimization,query-optimization,Sql Server,Performance,Sql Server 2008,Optimization,Query Optimization,我在重复Mongus Pong提出的问题,这个问题没有适合我的答案 我们中的大多数人都会发现,当嵌套查询达到一定的复杂性时,需要将其分解为临时表以保持其性能。荒谬的是这可能是最实际的前进方式,意味着这些过程不再成为一种观点。第三方BI应用程序通常只能很好地处理视图,因此这一点至关重要 我确信必须有一个简单的queryplan设置,使引擎只需依次运行每个子查询,从内到外工作。没有第二次猜测如何使子查询更具选择性(有时它会非常成功),也没有关联子查询的可能性。只是程序员希望通过括号之间的自包含代码返

我在重复Mongus Pong提出的问题,这个问题没有适合我的答案

我们中的大多数人都会发现,当嵌套查询达到一定的复杂性时,需要将其分解为临时表以保持其性能。荒谬的是这可能是最实际的前进方式,意味着这些过程不再成为一种观点。第三方BI应用程序通常只能很好地处理视图,因此这一点至关重要

我确信必须有一个简单的queryplan设置,使引擎只需依次运行每个子查询,从内到外工作。没有第二次猜测如何使子查询更具选择性(有时它会非常成功),也没有关联子查询的可能性。只是程序员希望通过括号之间的自包含代码返回的数据堆栈

我经常发现,简单地从子查询更改为#表需要120秒到5秒的时间。本质上,乐观主义者在某处犯了一个重大错误。当然,可能有非常耗时的方法,我可以劝说乐观主义者以正确的顺序查看表格,但即使这样也不能保证。我不是在这里要求理想的2秒执行时间,只是临时表格在视图的灵活性范围内提供给我的速度


我以前从未在这里发表过文章,但我已经写SQL很多年了,并且已经阅读了其他有经验的人的评论,他们也刚刚开始接受这个问题,现在我想让合适的天才站出来说,特别的提示是X…。

关于为什么会看到这种行为,有一些可能的解释。一些常见的是

  • 子查询或CTE可能会被重复重新计算
  • 将部分结果具体化到
    #temp
    表中,可以通过从等式中删除一些可能的选项,强制该部分计划采用更为优化的联接顺序
  • 将部分结果具体化到
    #temp
    表中,可以通过纠正较差的基数估计来改进计划的其余部分
  • 最可靠的方法是简单地使用
    #temp
    表并自己具体化它

    关于第1点,未能做到这一点,请参见。使用
    TOP(大号)。。。ORDER BY
    通常会鼓励对结果进行假脱机处理,而不是重复重新评估

    尽管如此,滑阀上也没有统计数据


    对于第2点和第3点,你需要分析为什么你没有得到想要的计划。可能重写查询以使用sargable谓词,或者更新统计信息可能会得到更好的计划。如果失败,您可以尝试使用查询提示来获得所需的计划。

    我不相信有查询提示指示引擎依次关闭每个子查询

    有一个
    选项(强制顺序)
    查询提示,它强制引擎按照指定的顺序执行连接,这可能会在某些情况下诱使引擎实现该结果。对于复杂的查询,这个提示有时会产生一个更有效的计划,并且引擎会坚持使用次优计划。当然,通常应该信任优化器来确定最佳计划


    理想情况下,会有一个查询提示,允许您将CTE或子查询指定为“物化”或“匿名临时表”,但没有。另一个选项(对于本文的未来读者)是使用用户定义的函数。多语句函数(如中所述)似乎强制SQL Server具体化子查询的结果。此外,它们允许您在结果表上指定主键和索引,以帮助查询优化器。然后,可以在select语句中使用此函数作为视图的一部分。例如:

    创建函数SalesByStore(@storeid varchar(30))
    返回@t TABLE(title varchar(80)非空主键,
    数量(不为空)为
    开始
    插入@t(标题、数量)
    选择t.标题,s.数量
    来自销售部
    在t.title\u id=s.title\u id上连接titles t
    其中s.stor_id=@storeid
    返回
    结束
    创建查看SalesData为
    从SalesByStore('6380')中选择*
    
    遇到这个问题后,我发现(在我的例子中)SQL Server以错误的顺序评估条件,因为我有一个可以使用的索引(
    TableFoo
    上的IDX\u CreatedOn)

    我通过强制子查询使用另一个索引(即在没有父查询的情况下执行子查询时使用的索引)来解决这个问题。在我的例子中,我切换到PK,这对查询没有意义,但允许首先计算子查询中的条件

    SELECT bar.*
    FROM
        (SELECT * FROM TableFoo WITH (INDEX([PK_Id]) WHERE Deleted = 1) foo
        JOIN TableBar bar ON (bar.FooId = foo.Id)
    WHERE
    foo.CreatedOn > DATEADD(DAY, -7, GETUTCDATE())
    
    通过
    Deleted
    列进行过滤非常简单,之后通过
    CreatedOn
    对少数结果进行过滤更加容易。通过比较子查询和父查询的实际执行计划,我能够找到答案



    一个更老套的解决方案(不是真正推荐的)是通过使用
    TOP
    限制结果来强制首先执行子查询,但是如果子查询的结果超过限制,这可能会在将来导致奇怪的问题(您总是可以将限制设置为荒谬的事情)。不幸的是,
    TOP 100%
    无法用于此目的,因为SQL Server只是忽略了它。

    您有一个长时间运行的查询示例吗?我可以粘贴它,但如果我说它不相关,请相信我。当查询只需要8秒就可以运行时,查询的复杂性与此相似,我所做的唯一更改是聚合的细节,它的运行时间达到了120秒。如果我临时处理子查询,它仍然运行得很快。我相信如果我把代码贴在这里,你会修好泰坦尼克号上的躺椅,克里蒂
    SELECT bar.*
    FROM
        (SELECT * FROM TableFoo WITH (INDEX([PK_Id]) WHERE Deleted = 1) foo
        JOIN TableBar bar ON (bar.FooId = foo.Id)
    WHERE
    foo.CreatedOn > DATEADD(DAY, -7, GETUTCDATE())