代码之家  ›  专栏  ›  技术社区  ›  soren.enemaerke

SQL Server查询计划差异

  •  6
  • soren.enemaerke  · 技术社区  · 16 年前

    当从参数化查询更改为非参数化查询时,我很难理解SQL Server中语句的估计查询计划的行为。

    我有以下疑问:

    DECLARE @p0 UniqueIdentifier = '1fc66e37-6eaf-4032-b374-e7b60fbd25ea'
    SELECT [t5].[value2] AS [Date], [t5].[value] AS [New]
    FROM (
        SELECT COUNT(*) AS [value], [t4].[value] AS [value2]
        FROM (
            SELECT CONVERT(DATE, [t3].[ServerTime]) AS [value]
            FROM (
                SELECT [t0].[CookieID]
                FROM [dbo].[Usage] AS [t0]
                WHERE ([t0].[CookieID] IS NOT NULL) AND ([t0].[ProductID] = @p0)
                GROUP BY [t0].[CookieID]
                ) AS [t1]
            OUTER APPLY (
                SELECT TOP (1) [t2].[ServerTime]
                FROM [dbo].[Usage] AS [t2]
                WHERE ((([t1].[CookieID] IS NULL) AND ([t2].[CookieID] IS NULL)) 
                OR (([t1].[CookieID] IS NOT NULL) AND ([t2].[CookieID] IS NOT NULL) 
                AND ([t1].[CookieID] = [t2].[CookieID]))) 
                AND ([t2].[CookieID] IS NOT NULL)          
                AND ([t2].[ProductID] = @p0)
                ORDER BY [t2].[ServerTime]
                ) AS [t3]
            ) AS [t4]
        GROUP BY [t4].[value]
        ) AS [t5]
    ORDER BY [t5].[value2]
    

    此查询由Linq2SQL表达式生成,并从LINQPad中提取。这将生成一个很好的查询计划(据我所知),并在大约10秒内在数据库上执行。但是,如果我用精确的值替换参数的两个用法,也就是用'='1fc66e37-6eaf-4032-b374-e7b60fbd25ea'替换两个'=@p0'部分,我会得到一个不同的估计查询计划,查询现在运行的时间要长得多(超过60秒,还没有看透)。

    我真正的问题是,我可以忍受10秒的执行时间(至少有一段时间),但我不能忍受60秒以上的执行时间。我的查询(如上所示)将由Linq2SQL生成,因此它将在数据库上按如下方式执行

    exec sp_executesql N'
            ...
            WHERE ([t0].[CookieID] IS NOT NULL) AND ([t0].[ProductID] = @p0)
            ...
            AND ([t2].[ProductID] = @p0)
            ...
           ',N'@p0 uniqueidentifier',@p0='1FC66E37-6EAF-4032-B374-E7B60FBD25EA'
    

    这会产生同样糟糕的执行时间(我认为这是非常奇怪的,因为这似乎是在使用参数化查询)。

    我不是在寻找关于创建哪些索引之类的建议,我只是想了解为什么在三个看似相似的查询上查询计划和执行如此不同。

    编辑: Heinz )使用不同的GUID here

    希望它能帮助你帮助我:)

    4 回复  |  直到 9 年前
        1
  •  2
  •   Quassnoi    16 年前

    我不是在寻找关于创建哪些索引之类的建议,我只是想了解为什么在三个看似相似的查询上查询计划和执行如此不同。

    您似乎有两个索引:

    IX_NonCluster_Config (ProductID, ServerTime)
    IX_NonCluster_ProductID_CookieID_With_ServerTime (ProductID, CookieID) INCLUDE (ServerTime)
    

    第一个索引不包括 CookieID ServerTime 因此,对于客户来说,这是更有效的 选择性的 ProductID 是的。E你拥有的那些(很多)

    更多 选择性的 产品ID

    平均而言,你 基数是这样的 SQL Server 期望第二种方法是有效的,这是在使用参数化查询或显式提供选择性查询时使用的方法 GUID 是的。

    然而,你的原版 指南 被认为选择性较低,这就是为什么使用第一种方法。

    不幸的是,第一种方法需要对 库基伊德

        2
  •  3
  •   Heinzi    16 年前

    如果提供显式值,SQL Server可以使用此字段的统计信息来做出“更好”的查询计划决策。不幸的是(正如我最近所经历的那样),如果统计数据中包含的信息具有误导性,有时SQL Server只是做出了错误的选择。

    如果您想深入研究这个问题,我建议您检查一下如果使用其他guid会发生什么:如果它对不同的具体guid使用不同的查询计划,这表明使用了统计数据。在这种情况下,您可能需要查看 sp_updatestats 和相关命令。

    编辑 DBCC SHOW_STATISTICS :直方图中的“慢”和“快”GUID可能位于不同的存储桶中。我已经 had a similar problem INDEX table hint 到SQL,它“引导”SQL Server找到“正确”的查询计划。基本上,我已经了解了在“快速”查询期间使用的索引,并将它们硬编码到SQL中。这远不是一个最优或优雅的解决方案,但我还没有找到更好的解决方案。。。

        3
  •  1
  •   Matt Wrock    16 年前

    我的猜测是,当您采用非参数化路由时,您的guid必须从varchar转换为UniqueIdentifier,这可能导致不使用索引,而它将用于采用参数化路由。

        4
  •  0
  •   Justin    16 年前

    不看执行计划就很难说

    正如我所说,SQLServer试图优化执行计划 对于 这个值,所以通常你会看到更好的结果。在这里,它的决策所依据的信息似乎是不正确的/误导性的,当它优化查询以获得通用参数值时(出于某种原因),您会感觉更好。

    推荐文章