代码之家  ›  专栏  ›  技术社区  ›  Rob Garrison

T/F:在过程中使用IF语句会生成多个计划

  •  4
  • Rob Garrison  · 技术社区  · 16 年前

    回应 this

    如果您使用的是SQL Server 2005或更高版本,则可以使用IFs在同一过程中进行多个查询,并且每个查询都会为其保存一个查询计划(相当于旧版本中每个查询的一个过程),请参阅我的答案中的文章或指向适当部分的链接:sommarskog.se/dyn-search-2005.html#if

    在早期版本的SQL Server中也可以这样做。

    在后来的研究中,我读了一段引文 here 来自Gert Drapers:

    因为SQL Server只允许每个存储过程有一个执行计划。。。

    我不知道那篇原始文章的日期,也不知道他所指的SQL Server版本。

    有没有人有可靠的参考资料来讨论这一点,或者更好的是,有一个测试来证明这一点?

    4 回复  |  直到 9 年前
        1
  •  6
  •   meklarian    16 年前

    更新#2:

    我还有一个步骤要补充,那应该会让一切变得更清楚。生成计划信息后,运行以下语句(使用正确的计划句柄)查看ShowPlan XML。

    DECLARE @val as VARBINARY(64)
    -- NOTE: Replace the Hex string with the current plan_handle !
    SET @val = CONVERT(VARBINARY(64), 0x05001300045A3D02B801BE11000000000000000000000000)
    SELECT * FROM sys.dm_exec_query_plan(@val)
    

    提及-在“执行计划”和“查询计划”之间存在差异。这似乎清楚地表明了我们在所有场景中实际看到的部分 查询。

    sys.dm_exec_query_stats at MSDN, SQL 2008

    sys.dm_exec_query_plan at MSDN, SQL 2008

    因此,有了所有这些信息,返回的计划句柄就是执行计划;这些部分是查询计划项。

    更新:

    gbn 的答案似乎是正确的,至少就我在这里测试的内容而言。有趣的东西。

    格特·德雷普斯的后一句话似乎是错误的——这是我的测试。我在这里使用的是SQL2005。在我的测试中,我看到为同一存储过程的不同部分生成了两个查询计划。

    首先,我创建了两个表, tblTag1 和 . 我使它们有点相似,这样我的存储过程就可以在两个表之间切换,并以相同的结果表布局返回结果。

    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    SET ANSI_PADDING ON
    GO
    -- Table #1, tblTag1
    CREATE TABLE [dbo].[tblTag1](
      [id] [int] IDENTITY(1,1) NOT NULL,
      [createDate] [datetime] NOT NULL CONSTRAINT [DF_tblTag1_createDate]  DEFAULT (getdate()),
      [someTag] [varchar](100) NOT NULL,
     CONSTRAINT [PK_tblTag1] PRIMARY KEY CLUSTERED 
    (
      [id] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    -- Table #2, tblTagWithGUID
    CREATE TABLE [dbo].[tblTagWithGUID](
      [id] [int] IDENTITY(1,1) NOT NULL,
      [createDate] [datetime] NOT NULL CONSTRAINT [DF_tblTagWithGUID_createDate]  DEFAULT (getdate()),
      [someTag] [varchar](100) NOT NULL,
      [someGUID] [uniqueidentifier] NOT NULL CONSTRAINT [DF_tblTagWithGUID_someGUID]  DEFAULT (newid()),
     CONSTRAINT [PK_tblTagWithGUID] PRIMARY KEY CLUSTERED 
    (
      [id] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    

    第二,存储过程。在存储过程中,我根据参数从一个或另一个表中进行选择。

    CREATE PROCEDURE spLoadTags
    @Pick AS BIT = NULL
    AS
    BEGIN
    
    IF @Pick = 0
    SELECT id, createDate, someTag FROM tblTag1
    
    IF @Pick = 1
    SELECT id, createDate, someTag FROM tblTagWithGUID
    
    END
    

    我向每个表中添加了一些数据,然后以0或1作为参数运行存储过程几十次。

    接下来,我运行这个查询来检查生成的查询计划。如果有人对我的草率行为感到不满,我很抱歉——当然,这不是生产代码。

    WITH PlanData AS
    (
    SELECT
    (SELECT SUBSTRING(text, statement_start_offset/2 + 1,
    (CASE WHEN statement_end_offset = -1 
    THEN LEN(CONVERT(nvarchar(MAX),text)) * 2 
    ELSE statement_end_offset 
    END - statement_start_offset)/2)
    FROM sys.dm_exec_sql_text(sql_handle) WHERE [text] like '%SELECT id, createDate, someTag FROM tblTag%') AS query_text,
    plan_handle
    FROM sys.dm_exec_query_stats  
    )
    SELECT 
    DISTINCT
    execution_count, 
    PlanData.query_text, 
    sys.dm_exec_query_stats.plan_handle
    FROM sys.dm_exec_query_stats, PlanData 
    WHERE 
    sys.dm_exec_query_stats.plan_handle = PlanData.plan_handle and 
    PlanData.query_text IS NOT NULL
    ORDER BY execution_count DESC
    

    当我运行这个查询时,我看到了一堆计划,但是因为我已经运行了几十次存储过程,不同的部分最终位于顶部。

    execution_count query_text  plan_handle
    96  SELECT id, createDate, someTag FROM tblTag1 0x05001200045A3D02B8613E13000000000000000000000000
    96  SELECT id, createDate, someTag FROM tblTagWithGUID  0x05001200045A3D02B8613E13000000000000000000000000
    

    我只包含了这两行,但希望这足够简单,其他人可以验证我的结果。如果您像我一样使用SQL管理工具,您可能会看到其他行;可能是浏览表格或其他活动引起的。

        2
  •  5
  •   gbn    16 年前

    我们都是对的:-)

    • “查询计划”在缓存中最多有2个条目:一个串行条目和一个并行条目

    • 如果对象不合格,则计划不同

    来自MSDN/BOL“ Execution Plan Caching and Reuse "

    "Obtaining Statement-Level Query Plans" from MS blog

        3
  •  2
  •   RBarryYoung    16 年前

    我相信这里所指的是存储过程有一个整体查询计划,该计划由各个语句的查询计划组成。换句话说,有一个与之相关的计划 每个 陈述 . (另外,您不需要使用EXEC(..)或sp_ExecuteSQL()来获取此信息)。

    因此,如果您使用if来分支到不同的查询语句,那么是的,您可以利用不同的计划。但是,如果您只是使用if设置不同的变量值,然后所有变量都由相同的SQL语句执行,使用这些变量,那么实际上您只有一个查询计划。

        4
  •  1
  •   Andrew    16 年前

    据我所知,每个存储过程或整个查询都会生成一个计划,根据SQL的哈希进行缓存,并在此基础上从缓存中检索。

    在此基础上,你可以得到多个计划。该条案文如下:

    Each subprocedure has its own plan in the cache, and for search_orders_4a_sub1 and sub2 that is a plan that is based on good input values from the first call. The catch-all search_orders_4a_sub3, still has a WITH RECOMPILE at it serves a mix of conditions.