SQL Профилировщик сервера против COALESCE (SP: STMTCOMPLETED) - PullRequest
1 голос
/ 08 мая 2020

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

Я бы ожидал, что каждая функция будет вызываться только один раз. Поэтому при профилировании с помощью событий SP: STMTCOMPLETED я ожидал увидеть только один набор записей с операторами из этого одного вызова.

Однако то, что я вижу, - это набор записей несколько раз, что говорит о том, что функция вызывается несколько раз.

Действительно ли функция вызывается дважды? И почему?

Вот сценарий для создания функции и хранимой процедуры:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[FOO] (@v INT)
RETURNS bit AS  
BEGIN
    RETURN 0
END
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[POO]
@v int,
@p varchar(100)
AS
SELECT 
    @p as p,
    CASE @p
        WHEN 'x' THEN COALESCE(dbo.FOO(@v), 0)
        ELSE 1
    END as poo
GO

Вот вызов процедуры

exec dbo.POO @v = 13911, @p = 'x'

Вот выходные данные Profiler, показывающие две строки для функции FOO и одна строка для процедуры POO:

enter image description here

Если я заменю вызов COALESCE таким оператором CASE:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[POO]
@v int,
@p varchar(100)
AS
SELECT 
    @p as p,
    CASE @p
        WHEN 'x' THEN CASE WHEN dbo.FOO(@v) = 1 THEN 1 ELSE 0 END
        ELSE 1
    END as poo
GO

Я получаю только одну строку для FOO в профилировщике:

enter image description here

Вот сценарий профилирования, созданный SQL Profiler (фильтрация по имени базы данных = dbTest):

/****************************************************/
/* Created by: SQL Server 2017 Profiler          */
/* Date: 05/08/2020  12:06:04 AM         */
/****************************************************/


-- Create a Queue
declare @rc int
declare @TraceID int
declare @maxfilesize bigint
set @maxfilesize = 5 

-- Please replace the text InsertFileNameHere, with an appropriate
-- filename prefixed by a path, e.g., c:\MyFolder\MyTrace. The .trc extension
-- will be appended to the filename automatically. If you are writing from
-- remote server to local drive, please use UNC path and make sure server has
-- write access to your network share

exec @rc = sp_trace_create @TraceID output, 0, N'InsertFileNameHere', @maxfilesize, NULL 
if (@rc != 0) goto error

-- Client side File and Table cannot be scripted

-- Set the events
declare @on bit
set @on = 1
exec sp_trace_setevent @TraceID, 45, 1, @on
exec sp_trace_setevent @TraceID, 45, 9, @on
exec sp_trace_setevent @TraceID, 45, 3, @on
exec sp_trace_setevent @TraceID, 45, 4, @on
exec sp_trace_setevent @TraceID, 45, 5, @on
exec sp_trace_setevent @TraceID, 45, 6, @on
exec sp_trace_setevent @TraceID, 45, 7, @on
exec sp_trace_setevent @TraceID, 45, 8, @on
exec sp_trace_setevent @TraceID, 45, 10, @on
exec sp_trace_setevent @TraceID, 45, 11, @on
exec sp_trace_setevent @TraceID, 45, 12, @on
exec sp_trace_setevent @TraceID, 45, 13, @on
exec sp_trace_setevent @TraceID, 45, 14, @on
exec sp_trace_setevent @TraceID, 45, 15, @on
exec sp_trace_setevent @TraceID, 45, 16, @on
exec sp_trace_setevent @TraceID, 45, 17, @on
exec sp_trace_setevent @TraceID, 45, 18, @on
exec sp_trace_setevent @TraceID, 45, 22, @on
exec sp_trace_setevent @TraceID, 45, 25, @on
exec sp_trace_setevent @TraceID, 45, 26, @on
exec sp_trace_setevent @TraceID, 45, 28, @on
exec sp_trace_setevent @TraceID, 45, 29, @on
exec sp_trace_setevent @TraceID, 45, 34, @on
exec sp_trace_setevent @TraceID, 45, 35, @on
exec sp_trace_setevent @TraceID, 45, 41, @on
exec sp_trace_setevent @TraceID, 45, 48, @on
exec sp_trace_setevent @TraceID, 45, 49, @on
exec sp_trace_setevent @TraceID, 45, 50, @on
exec sp_trace_setevent @TraceID, 45, 51, @on
exec sp_trace_setevent @TraceID, 45, 55, @on
exec sp_trace_setevent @TraceID, 45, 60, @on
exec sp_trace_setevent @TraceID, 45, 61, @on
exec sp_trace_setevent @TraceID, 45, 62, @on
exec sp_trace_setevent @TraceID, 45, 64, @on
exec sp_trace_setevent @TraceID, 45, 66, @on


-- Set the Filters
declare @intfilter int
declare @bigintfilter bigint

exec sp_trace_setfilter @TraceID, 35, 0, 6, N'dbTest'
-- Set the trace status to start
exec sp_trace_setstatus @TraceID, 1

-- display trace id for future references
select TraceID=@TraceID
goto finish

error: 
select ErrorCode=@rc

finish: 
go

1 Ответ

1 голос
/ 08 мая 2020

Да, его называют дважды.

Он расширяется до CASE WHEN dbo.FOO(@v) IS NOT NULL THEN dbo.FOO(@v) ELSE 0 END

Обе ссылки на dbo.FOO оцениваются отдельно.

Вы можете использовать ISNULL(dbo.FOO(@v), 0) вместо этого, чтобы избежать этого.

См. Неоправданно плохая производительность для coalesce (подзапрос) для получения дополнительной информации (перенесено со старого сайта Connect на новый сайт обратной связи)

Кстати, со скалярным встраиванием UDF в 2019 году это не так. В конечном итоге функция не вызывается вообще во время выполнения - в этом простом примере все упрощается до CASE WHEN [@p]='x' THEN (0) ELSE (1) END (поскольку он видит, что функция всегда возвращает 0 и просто заменяет это значение).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...