标量函数在存储过程中使用时在 SQL Server 中发生请求超时问题

Posted

技术标签:

【中文标题】标量函数在存储过程中使用时在 SQL Server 中发生请求超时问题【英文标题】:Scalar function occurring request timeout issue in SQL Server while using in stored procedure 【发布时间】:2017-05-23 10:08:48 【问题描述】:

这是 Salar 函数。我在带有多个参数的存储过程中使用它。对于小的结果,这很好,但对于大数据,它会超时并且执行时间也很长。请分享一些其他替代或一些增强功能

ALTER FUNCTION [dbo].[FNGETMULIPLEASSIGNESS_NEW2]
(
  @TIMELINEID INT,
  @MILSTONEID INT,
  @TASKID INT
)

RETURNS varchar(max)
AS
  BEGIN
        DECLARE @Assignees varchar(max)='', @isExists bit=0
   if(@TASKID=0)
        BEGIN
        Select  @Assignees = @Assignees+ FIRSTNAME +' ' + LASTNAME+', '  
            FROM CASETIMELINEPEOPLE 
            INNER JOIN USERDETAIL  ON 
            CASETIMELINEPEOPLE.PEOPLEUSERID=USERDETAIL.USERID
            WHERE (CASETIMELINEID= @TIMELINEID) AND 
            (TEMPLATEMILESTONEID=@MILSTONEID) AND 
            (TEMPLATETASKID is null) and CASETIMELINEPEOPLE.isdeleted=0
    END

  else

        BEGIN
            Select  @Assignees = @Assignees+ FIRSTNAME +' ' + LASTNAME+','  
            FROM CASETIMELINEPEOPLE 
            INNER JOIN USERDETAIL  ON 
            CASETIMELINEPEOPLE.PEOPLEUSERID=USERDETAIL.USERID
            WHERE (CASETIMELINEID= @TIMELINEID) AND 
            (TEMPLATEMILESTONEID=@MILSTONEID) AND 
            (TEMPLATETASKID=@TASKID) and CASETIMELINEPEOPLE.isdeleted=0

       END

        SELECT @Assignees=SUBSTRING(@Assignees, 0,LEN(@Assignees))  
        RETURN @Assignees
END

【问题讨论】:

我没有看到太多改进 SELECT 的替代方案。也许通过引用字段向表中添加一些索引可能会有所帮助。 您可以通过WHERE COALESCE(TEMPLATETASKID, 0) = @TASKID 避免 if/else(或者只是条件中的普通 OR) 感谢酒精饮料。那里缺少索引 【参考方案1】:

使用内联表值函数将提高性能。

参考:

When is a SQL function not a function? "If it’s not inline, it’s rubbish." - Rob Farley Inline Scalar Functions - Itzik Ben-Gan Scalar functions, inlining, and performance: An entertaining title for a boring post - Adam Machanic TSQL User-Defined Functions: Ten Questions You Were Too Shy To Ask - Robert Sheldon


这是使用stuff() with select ... for xml path ('') method of string concatenation 的标量函数的内联表值函数版本:
create function dbo.fn_get_multiple_assigness_itvf (
    @timelineid int
  , @milstoneid int
  , @taskid int
) returns table as return (
select Assignees = stuff((
    select ',' + firstname + ' ' + lastname
    from casetimelinepeople ctp
      inner join userdetail ud
        on ctp.peopleuserid=ud.userid
    where casetimelineid = @timelineid
      and templatemilestoneid = @milstoneid
      and (templatetaskid = @taskid 
        or (@taskid = 0 and templatetaskid is null)
        )
      and ctp.isdeleted=0
    for xml path (''), type).value('.','nvarchar(max)')
  ,1,1,'')
)
go

rextester 演示:http://rextester.com/UZTJS46485

测试设置:

create table casetimelinepeople (
    casetimelineid int
  , peopleuserid int
  , templatemilestoneid int
  , templatetaskid int
  , isdeleted bit not null default 0
);
insert into casetimelinepeople values
 (1,1,1,null,0)
,(1,2,1,null,0)
,(1,3,1,null,0)
,(1,2,1,1,0)
,(1,3,1,1,0)

create table userdetail (
    userid int not null
  , firstname varchar(32) not null
  , lastname varchar(32) not null);
insert into userdetail values
 (1, 'Some', 'One')
,(2, 'Avinash', 'Raikwar')
,(3, 'Sql','Zim');
go

并像这样查询内联表值函数:

select * 
from dbo.fn_get_multiple_assigness_itvf(1,1,0) 

返回

+----------------------------------+
|            Assignees             |
+----------------------------------+
| Some One,Avinash Raikwar,Sql Zim |
+----------------------------------+

select * 
from dbo.fn_get_multiple_assigness_itvf(1,1,1)

返回:

+-------------------------+
|        Assignees        |
+-------------------------+
| Avinash Raikwar,Sql Zim |
+-------------------------+

使用cross apply() 为查询中的每一行调用函数:

select *
from casetimelinepeople ctp
  cross apply dbo.fn_get_multiple_assigness_itvf(
      ctp.casetimelineid
    , ctp.templatemilestoneid
    , ctp.templatetaskid
    ) x

返回:

+----------------+--------------+---------------------+----------------+-----------+----------------------------------+
| casetimelineid | peopleuserid | templatemilestoneid | templatetaskid | isdeleted |            Assignees             |
+----------------+--------------+---------------------+----------------+-----------+----------------------------------+
|              1 |            1 |                   1 | NULL           | False     | Some One,Avinash Raikwar,Sql Zim |
|              1 |            2 |                   1 | NULL           | False     | Some One,Avinash Raikwar,Sql Zim |
|              1 |            3 |                   1 | NULL           | False     | Some One,Avinash Raikwar,Sql Zim |
|              1 |            2 |                   1 | 1              | False     | Avinash Raikwar,Sql Zim          |
|              1 |            3 |                   1 | 1              | False     | Avinash Raikwar,Sql Zim          |
+----------------+--------------+---------------------+----------------+-----------+----------------------------------+

【讨论】:

Rob Farley 的那篇文章非常棒。感谢您发布它。 @AlanBurstein 很高兴你喜欢它!

以上是关于标量函数在存储过程中使用时在 SQL Server 中发生请求超时问题的主要内容,如果未能解决你的问题,请参考以下文章

从标量 SQL 函数返回两个值

如何使用sql server数据库中的标量值函数

如何在sql server中的标量函数中传递列名[关闭]

SQL 过早退出标量函数

SQL Server如何定位自定义标量函数被那个SQL调用次数最多浅析

如何在 MS Access VB 中访问 SQL Server 标量函数