SQL 查询优化

Posted

技术标签:

【中文标题】SQL 查询优化【英文标题】:SQL Query optimization 【发布时间】:2011-02-21 13:21:29 【问题描述】:

我对查询有一些疑问。我在第一页中调用了这个存储过程,所以它是否足够优化对我来说很重要。

我用一些基本的where 表达式做一些选择,然后用我通过这个存储过程的一些表达式过滤它们。 选择top n 对我来说也很重要,它会搜索数百万个项目(但我已经有数百个项目),然后在我的网站上进行一些分页。

Select top (@NumberOfRows) 
    ...     
from(
    SELECT  
            row_number() OVER (ORDER BY tblEventOpen.TicketAt, tblEvent.EventName, tblEventDetail.TimeStart) as RowNumber
            , ...                    
    FROM    --[...some inner join logic...]
    WHERE     
            (tblEventOpen.isValid = 1) AND (tblEvent.isValid = 1) and 
            (tblCondition_ResellerDetail.ResellerID = 1) AND 
            (tblEventOpen.TicketAt >= GETDATE()) AND 
            (GETDATE() BETWEEN 
                            DATEADD(minute, (tblEventDetail.TimeStart - 60 * tblCondition_ResellerDetail.StartTime) , tblEventOpen.TicketAt) 
                        AND DATEADD(minute, (tblEventDetail.TimeStart - 60 * tblCondition_ResellerDetail.EndTime) , tblEventOpen.TicketAt))
) as t1
where RowNumber >= (@PageNumber -1) * @NumberOfRows and 
    (@city='' or @city is null or city like @city) and 
    (@At is null or @At=At) and 
    (@TimeStartInMinute=-1 or @TimeStartInMinute=TimeStartInMinute) and
    (@EventName='' or EventName like @EventName) and
    (@CategoryID=-1 or @CategoryID = CategoryID) and
    (@EventID is null or @EventID = EventID) and
    (@DetailID is null or @DetailID = DetailID)
ORDER BY RowNumber

    我很担心这部分:

    (GETDATE() BETWEEN DATEADD(minute, (tblEventDetail.TimeStart - 60 * tblCondition_ResellerDetail.StartTime) , tblEventOpen.TicketAt) AND DATEADD(minute, (tblEventDetail.TimeStart - 60 * tblCondition_ResellerDetail.EndTime) , tblEventOpen.TicketAt))t1 是如何执行的?我的意思是在 t1 之后(第 17 行及以后)放置了一些 where 表达式之后,它会在 t1 执行后过滤项目吗?例如,我按行号 10 过滤结果,所以这意味着内部 (...) as t1 选择将只返回 10 个项目,或者它选择所有项目然后我的外部选择将采用其中的 10 个?

    我想通过一些可选参数过滤我的结果,所以我放了@DetailID is null or @DetailID = DetailID之类的东西,这是一个好方法吗?

    我还应该考虑其他什么来让它更快(更优化)?

【问题讨论】:

这里不那么容易看出哪些是大桌子。有没有看过查询优化计划。您是否有大量的索引搜索或索引扫描?您应该针对索引搜索。上面 1. 的一个建议是在查询之前计算出到日期和起止日期并将它们存储在变量中。 我正在学习,我没有阅读任何查询优化计划,如果您可以提供我一个:)。我目前没有使用任何索引计划。我这里最大的桌子是tblEventOpen,但我认为它没有帮助。对于我的第一个问题,谢谢,我将使用 @today 变量而不是 GetDate() 您需要在查询之前运行 EXPLAIN 命令以获取有关数据库决定索引的索引的信息,所以当您说您没有使用任何索引计划时,这不是您做的事情,数据库引擎做到了!当您使用 EXPLAIN 时,您将从数据库中获得关于它使用哪些索引的结果,以帮助您决定是否需要根据查询条件创建不同的索引。 @Leslie:我想是 T-SQL,它没有 EXPLAIN 语句。 EXPLAIN 不像 Andriy 所说的那样工作,或者我应该把它放在特别的地方?! 【参考方案1】:

我对您的查询的评论:

    你是对的,你应该担心条件“GETDATE() BETWEEN ...”。将值与涉及超过 1 个表的函数进行比较很可能会扫描整个搜索空间。简化您的条件,或者在可能的情况下为此类函数添加计算列 将除“RowNumber >= ...”之外的所有条件放入内部查询中 可以按照您的方式设置可选条件。我也这样做:-) 确保对于 where 子句中使用的每一列,至少有一个索引作为索引的第一列,然后是主键。如果你的主键是集群的会更好

嗯,这些都是基于我自己的经验。它可能适用于您的情况,也可能不适用。

[更新]这是完整的查询

Select top (@NumberOfRows)
    ...    
from(
    SELECT  
            row_number() OVER (ORDER BY tblEventOpen.TicketAt, tblEvent.EventName, tblEventDetail.TimeStart) as RowNumber
            , ...                    
    FROM    --[...some inner join logic...]
    WHERE    
            (tblEventOpen.isValid = 1) AND (tblEvent.isValid = 1) and
            (tblCondition_ResellerDetail.ResellerID = 1) AND
            (tblEventOpen.TicketAt >= GETDATE()) AND
            (GETDATE() BETWEEN
                            DATEADD(minute, (tblEventDetail.TimeStart - 60 * tblCondition_ResellerDetail.StartTime) , tblEventOpen.TicketAt)
                        AND DATEADD(minute, (tblEventDetail.TimeStart - 60 * tblCondition_ResellerDetail.EndTime) , tblEventOpen.TicketAt)) and
            (@city='' or @city is null or city like @city) and
            (@At is null or @At=At) and
            (@TimeStartInMinute=-1 or @TimeStartInMinute=TimeStartInMinute) and
            (@EventName='' or EventName like @EventName) and
            (@CategoryID=-1 or @CategoryID = CategoryID) and
            (@EventID is null or @EventID = EventID) and
            (@DetailID is null or @DetailID = DetailID)
) as t1
where RowNumber >= (@PageNumber -1) * @NumberOfRows
ORDER BY RowNumber

【讨论】:

感谢您的回答,我正在等待完整的答案(抱歉我的延误)。那么2号有必要吗?它有助于我的查询运行得更快吗? 恐怕我没听懂你所说的4th 解决方案是什么意思。是否有必要将我的 where 子句带入我的内部查询?我必须注意到你,我可以只用一个查询来写这个商店!!但它会降低可读性和代码维护。所以我要问的是我的第一个问题。请在@Chris 的另一个给定答案中阅读我的 cmets。感谢您的宝贵时间【参考方案2】:

虽然您可以就查询寻求建议,但最好自己学习如何优化它。

您需要查看执行计划,找出瓶颈,然后查看是否有任何可以改进的地方。

在 SSMS 中,您可以在运行查询之前单击“查询”--->“包括实际执行计划”。 (Ctrl+M) 是它们的键盘快捷键。

然后执行您的查询。 SSMS 将在结果窗格中创建一个新选项卡。这将向您展示 SQL 引擎如何执行您的查询,您可以将鼠标悬停在每个节点上以获取更多信息。 cost % 将特别有趣,让您可以查看查询中最昂贵的部分。

没有那个执行计划就很难再给你建议了,这就是为什么很多人对你的问题发表评论的原因。您的架构和索引会改变查询的执行方式,因此如果没有表/索引等脚本,某人无法在自己的环境中准确地复制它......即使这样,统计数据也可能过时并且可能会出现其他问题。

您还可以执行SET STATISTICS PROFILE ON 以获取计划的文本视图(可能有助于寻求帮助)。

有许多文章可以帮助您解决瓶颈问题,或者发布另一个问题以获得更多建议。

http://msdn.microsoft.com/en-us/library/ms178071.aspx

SQL Server Query Plan Analysis

Execution Plan Basics

【讨论】:

+1 表示 (Ctrl+M)。我认为Set statistics profile on 有点没用,因为我对分析查询一无所知,它充满了估计值(以及totalsubtreecost,如果它告诉我它在子查询上花费了多少时间,我认为这可能很有用),或者我不知道如何利用它。我没有带来架构或索引信息,因为我不想专注于这个特定的查询。事实上,我只是想展示我个人是如何写我的商店的,以及这种写作是否有问题...... 我的主要问题是我的first 问题,也就是说,如果我有select * from (BIG_QUERY_WITH_WHERE) where store_filters_goes_here,我是否真的应该关注outer where 表达式并将其放在我的BIG_QUERY_WITH_WHERE 中?如果可能的话,我需要某种教授:D

以上是关于SQL 查询优化的主要内容,如果未能解决你的问题,请参考以下文章

数据库牛人是如何进行SQL优化的?

SQL多个表联合查询优化的问题

DBA的五款最佳SQL查询优化工具

优化SQL查询:如何写出高性能SQL语句

SQL优化----百万数据查询优化

一文终结SQL 子查询优化