sql - 电话号码 : Using IF statements in a procedure produces multiple plans

标签 sql sql-server tsql stored-procedures

回复this问题,KM 说

if you are on or above SQL Server 2005, you can use IFs to have multiple queries in the same procedure and each will have an query plan saved for it (equiv to a procedure for each on older versions), see the article in my answer or this link to proper section: sommarskog.se/dyn-search-2005.html#IF

添加了 HLGEM

You can do that in earlier versions of SQL Server as well.

我阅读了 Sommarskog 优秀文章的这一部分,但没有看到任何有关多个计划的内容。

在后来的研究中,我读到了一句话here来自格特·德雷珀斯:

Because SQL Server allows only one execution plan per stored procedure ...

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

有人有可靠的引用资料来讨论这个问题,或者更好的是,有一个测试可以证明这是真的吗?

最佳答案

更新#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)

查看生成的 XML,可以发现有 2 个 QueryPlan 元素、2 个或更多 StmtSimple/StmtCond 元素,并且总共只有 1 个 Batch。正如 gbn 提到的 - “执行计划”和“查询计划”之间存在差异。这似乎让我们在所有 sys.dm_ 查询中实际查看的部分变得非常清楚。

sys.dm_exec_query_stats at MSDN, SQL 2008

sys.dm_exec_query_plan at MSDN, SQL 2008

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

--

更新:

在安德鲁发表评论后,我重新测试,确实查询计划句柄是相同的。而且,为了确定 - 我创建了存储过程的副本,仅更改名称,然后针对该存储过程重新运行测试。这还导致生成一组新的查询部分,它们共享相同的计划句柄。

因此,gbn 的答案似乎是正确的,至少根据我在这里测试的结果来看是这样。有趣的东西。

--

格特·德雷珀斯 (Gert Drapers) 的后一句话似乎是错误的 - 这是我的测试。我这里使用的是 SQL 2005。在我的测试中,我看到为同一存储过程的不同部分生成了 2 个查询计划。

首先,我创建了两个表:tblTag1tblTagWithGUID。我使它们有些相似,以便我的存储过程可以在任一表之间交替并返回具有相同结果表布局的结果。

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 管理工具,你可能会看到其他行;可能是由浏览表格或其他事件引起的。

关于sql - 电话号码 : Using IF statements in a procedure produces multiple plans,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1510489/

相关文章:

SQL命令执行多次?

mysql - 如何在(MYSQL和SQL Server)多列中搜索一个值而无需迭代/循环/游标?

sql - XML 到 SQL - SQL Server

MySql 插入重复键更新 - 唯一

sql-server - 添加参数时SSIS性能下降

SQL累进和

sql-server - 哪个免费数据库(SQL Express 除外)的 SQL 语法与 T-SQL 最相似?

mysql - 有没有办法在 MySQL 中执行 INSERT 或 SELECT?

sql - 使用每行一次的完全外连接

java - hsqldb 脚本文件出现唯一约束违规异常(未读取空格字符)