T/F: Using IF statements in a procedure produces multiple plans

前端 未结 4 1689
轮回少年
轮回少年 2021-01-01 04:53

In responses to this question, KM said

if you are on or above SQL Server 2005, you can use IFs to have multiple queries in the same procedure and each

4条回答
  •  离开以前
    2021-01-01 05:11

    Update #2:

    I have one more step to add that ought to make everything clearer. After generating plan information, run the following statement (with the correct plan handle) to look at the 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)
    

    Looking at the generated XML reveals that there are 2 QueryPlan elements, 2 or more StmtSimple/StmtCond elements, and only 1 Batch overall. As gbn mentions- there is a difference between "Execution Plans" and "Query Plans". This seems to make it crystal clear what parts we are actually viewing in all the sys.dm_ queries.

    sys.dm_exec_query_stats at MSDN, SQL 2008

    sys.dm_exec_query_plan at MSDN, SQL 2008

    So with all this information, the plan_handle returned is the Execution Plan; and the parts are Query Plan items.

    --

    Update:

    After Andrew's comment, I retested and indeed the query plan handles are the same. And, just to be sure- I created a duplicate of the stored procedure, changing only the name, and reran my test against that stored procedure. This also caused a new set of query parts to be generated, that shared the same plan handle.

    So, gbn's answer appears to be correct, at least with what I've tested here. Interesting stuff.

    --

    The latter quote from Gert Drapers appears to be false- here is my test. I'm using SQL 2005 here. In my test, I see 2 query plans generated for different parts of the same stored procedure.

    First, I created two tables, tblTag1 and tblTagWithGUID. I made them somewhat similar so that my stored procedure can alternate between either table and return results with an identical result table layout.

    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
    

    Second, the stored procedure. In the stored procedure I select from one or the other table depending on the argument.

    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
    

    I added some data to each table- then ran the stored procedure a few dozen times with 0 or 1 as the argument.

    Next, I ran this query to check up on the generated query plans. I'm sorry if anyone is offended with my sloppiness here- this isn't production code, of course.

    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
    

    When I run this query, I see a bunch of plans, but because I've run the stored procedure a few dozen times, the distinct parts end up at the top.

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

    I've only included those two rows, but hopefully this is straightforward enough where someone else can verify my results. You may see other rows if you are using the SQL management tool like I am; presumably caused by browsing tables or other activity.

提交回复
热议问题