SQL Server Profiler vs COALESCE (SP:STMTCOMPLETED)

问题描述 投票:0回答:1

我在运行跟踪时发现了一些我不明白的地方。我有一个存储过程,其中有一个简单的选择语句,从一系列函数调用中返回第一个非空值。

我希望每个函数只被调用一次。因此,当使用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

sql-server database-performance sql-server-profiler sql-trace
1个回答
1
投票

是的,它被调用了两次。

它被扩展为 CASE WHEN dbo.FOO(@v) IS NOT NULL THEN dbo.FOO(@v) ELSE 0 END

两次提到 dbo.FOO 分别进行评估。

您可以使用 ISNULL(dbo.FOO(@v), 0) 而不是为了避免这种情况。

coalesce(subquery)的性能过于糟糕。 更多信息(从旧的连接网站迁移到新的反馈网站)。

另外,在2019年使用标量UDF内联时,最终在运行时根本不会调用函数--整个事情被简化成了 CASE WHEN [@p]='x' THEN (0) ELSE (1) END 在这个简单的例子中(因为它看到函数总是回到 0 而只是将该值代入)。)

© www.soinside.com 2019 - 2024. All rights reserved.