T / F: использование операторов IF в процедуре дает несколько планов - PullRequest
4 голосов
/ 02 октября 2009

В ответах на этот вопрос КМ сказал

если вы используете SQL Server 2005 или более позднюю версию, вы можете использовать IF для нескольких запросов в одной и той же процедуре, и для каждого из них будет сохранен план запросов (эквивалентный процедуре для каждого в более старых версиях), см. Статью в моем ответе или этой ссылке на соответствующий раздел: sommarskog.se/dyn-search-2005.html#IF

HLGEM добавлено

Это можно сделать и в более ранних версиях SQL Server.

Я читал этот раздел превосходной статьи Соммарскога, но ничего не видел о нескольких планах.

В более позднем исследовании я прочитал цитату здесь от Герт Дрэперс:

Поскольку SQL Server допускает только один план выполнения для хранимой процедуры ...

Я не знаю дату этой оригинальной статьи или версию SQL Server, на которую он ссылается.

Есть ли у кого-нибудь надежная справка, которая обсуждает это или, еще лучше, тест, который доказывает, что это правда?

Ответы [ 4 ]

6 голосов
/ 02 октября 2009

Обновление № 2:

Я хочу добавить еще один шаг, который должен прояснить все. После создания информации о плане выполните следующую инструкцию (с правильным дескриптором плана), чтобы посмотреть XML ShowPlan.

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 пакетный пакет. Как упоминает gbn , существует разница между «планами выполнения» и «планами запросов». Похоже, это делает кристально ясным, какие части мы на самом деле просматриваем во всех запросах sys.dm_ .

sys.dm_exec_query_stats в MSDN, SQL 2008

sys.dm_exec_query_plan в MSDN, SQL 2008

Таким образом, со всей этой информацией, возвращенный plan_handle является Планом выполнения; и части являются элементами плана запроса.

-

Обновление:

После комментария Эндрю я еще раз проверил, и действительно, дескрипторы плана запроса совпадают. И, чтобы быть уверенным, я создал дубликат хранимой процедуры, изменив только имя, и перезапустил свой тест для этой хранимой процедуры. Это также привело к созданию нового набора частей запроса, которые совместно использовали один и тот же дескриптор плана.

Итак, ответ gbn кажется правильным, по крайней мере, с тем, что я здесь тестировал. Интересные вещи.

-

Последняя цитата из Герт Дрэперс, похоже, неверна - вот мой тест. Я использую SQL 2005 здесь. В моем тесте я вижу 2 плана запросов, сгенерированных для разных частей одной и той же хранимой процедуры.

Сначала я создал две таблицы: tblTag1 и tblTagWithGUID . Я сделал их несколько похожими, чтобы моя хранимая процедура могла чередоваться между любой таблицей и возвращать результаты с идентичным расположением таблицы результатов.

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, как я; предположительно вызвано просмотром таблиц или другими действиями.

5 голосов
/ 02 октября 2009

Мы все правы: -)

  • «План запроса» содержит не более 2 записей в кеше: одну последовательную и одну параллельную

  • У каждого пользователя есть свой «контекст выполнения», который запускает план

  • Планы отличаются, если объекты не квалифицированы

Итак, вы можете подумать, что план - это не потому, что таблицы не соответствуют схеме (что в SQL Server 2000, 2005 и 2008 одинаково)

Из MSDN / BOL " Кэширование и повторное использование плана выполнения "

Редактировать:

«Получение планов запросов на уровне выписки» из блога MS

2 голосов
/ 02 октября 2009

Я полагаю, что имеется в виду, что хранимая процедура имеет общий план запросов, который состоит из планов запросов для отдельных операторов. Другими словами, существует план, связанный с каждым оператором . (И AFAIK, вам не нужно использовать EXEC (..) или sp_ExecuteSQL (), чтобы получить это).

Так что, если вы используете IF для перехода к различным операторам запроса, тогда да, вы можете использовать разные планы. Однако, если вы просто используете IF, чтобы установить разные значения переменных, которые затем будут выполняться одним и тем же оператором SQL с использованием этих переменных, то НЕТ, у вас фактически только один план запроса.

1 голос
/ 02 октября 2009

Насколько я знаю, я бы сказал, что каждая хранимая процедура или общий запрос генерирует один план, который кэшируется на основе хэша SQL и извлекается из кеша на этой основе.

Я полагаю, что речь шла о том, когда вы писали процесс, содержащий вызовы, используя sp_executesql или exec, поскольку тогда это был бы дополнительный отправленный запрос, генерирующий его собственный план.

На этом основании вы получаете несколько планов. Текст в статье был следующим:

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.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...