Phi*_*ipD 7 sql-server cte functions sql-server-2019
我们在 SQL Server 2016 数据库中有一些已经投入生产一段时间的 SQL 代码;但在重新启动 SQL Server 后的第一个小时左右(从 5-10 分钟到一个小时或更长时间,可能取决于 SQL Server 中的活动级别),它会在 SQL Server 2019 数据库中引发错误。错误是 CTE(公用表表达式)的“对象名称无效”。
我们在 SQL Server 2016 中有一个包含多个数据库的生产环境。我们现在已经使用 SQL Server 2019(在 Windows Server 2016 机器上,具有 24GB 的 RAM 和 4 个 CPU 内核)设置了一个新的开发/测试环境,以便我们可以进行测试使用 SQL Server 2019。此测试服务器上的数据库是从生产备份中恢复的生产数据库副本。测试环境中的所有数据库的兼容级别都设置为 150(SQL Server 2019)。
每天清晨,我们开始看到一些使用 CTE 的函数存在一些问题,这些函数会引发如下错误:
SqlException (0x80131904): Invalid object name 'CTEuniqueName'.]
Msg 208, Level 16, State 1, Procedure ufn_FunctionName, Line 28 [Batch Start Line 0]
Invalid object name 'CTEuniqueName'.
Run Code Online (Sandbox Code Playgroud)
错误在短时间内停止发生,直到第二天早上才再次发生。
错误发生在一对一个接一个调用的存储过程中,它们都调用了相同的(用户定义的 SQL)函数。通过一些测试,我了解到有时只需调用该函数,然后仅从该函数执行一段代码,就可能导致相同的错误。
我还发现,通过重新启动 SQL Server 实例并调用函数或代码块,我可能会持续导致错误。这可能也是它只在清晨失败的原因 - 在此之前的几个小时内 SQL Server 实例上没有任何活动,因此它进入了空闲模式或关闭了其进程。
在反复调用函数或代码后,在某些时候它会成功而不会引发错误,之后,它似乎继续工作(直到我再次重新启动 SQL Server 实例)。
如果我将包含此函数的数据库更改为“SQL Server 2016”兼容模式,则该函数始终会成功,即使在重新启动 SQL Server 实例后也是如此。因此,这似乎是 SQL Server 2019 特有的问题。
函数中的代码如下所示(使用匿名名称),该函数包含 2 个独立的“IF”块,类似于以下内容;但是我可以仅使用以下代码块导致错误。此代码中的 2 个视图是同一 SQL Server 实例中其他数据库的视图。
DECLARE @MyID INT = 150589;
DECLARE @MyType VARCHAR(25) = 'Test';
DECLARE @ExpirationDate DATE;
BEGIN
IF @MyType = 'Test'
BEGIN
DECLARE @Test1 INT;
WITH CTEuniqueName(PersonID, DateComplete)
AS (SELECT T1.PersonID, MAX(T2.DateComplete) AS DateComplete
FROM dbo.TABLE1 AS T1 WITH(NOLOCK)
LEFT JOIN dbo.VIEW2 AS T2 WITH(NOLOCK) ON T2.ID = T1.ID
WHERE T1.ID = @MyID
AND T2.SecondID IN(SELECT SecondID
FROM dbo.TABLE3 WITH(NOLOCK)
WHERE Name = 'TEST')
GROUP BY T1.PersonID)
SELECT @ExpirationDate = CASE
WHEN V3.TimeFrame > 0 THEN DATEADD(DAY, TimeFrame, CONVERT(DATE, CTE1.DateComplete))
ELSE NULL
END
FROM CTEuniqueName AS CTE1
INNER JOIN dbo.VIEW2 AS V2 WITH(NOLOCK) ON V2.ID = CTE1.ID
AND V2.DateComplete = CTE1.DateComplete
INNER JOIN dbo.VIEW3 AS V3 WITH(NOLOCK) ON V3.QuizID = V2.QuizID
WHERE TS.SecondID IN(SELECT SecondID
FROM dbo.TABLE3 WITH(NOLOCK)
WHERE Name = 'TEST');
END;
END;
Run Code Online (Sandbox Code Playgroud)
此外,在启动后发生错误的时间段内,如果我在 SSMS 窗口中运行 2 个存储过程(调用上述函数,并且两个过程都返回一个结果集),那么这些过程成功返回各自的结果集在 SSMS 窗口中,然后 SSMS 切换到消息面板并显示“无效对象名称”错误。
有谁知道为什么这可能仅在 SQL Server 2019 中间歇性失败(并且似乎仅在 SQL Server 启动或一段时间空闲时间之后)?或者,任何人都可以建议如何解决此类问题?我试过检查 SQL 错误日志;而且我还尝试运行包含函数调用的 Profiler - 这些都没有提供任何线索。
附加信息:
DBCC FREESYSTEMCACHE(N'ALL');确实会导致暂时再次出现相同的错误(与重新启动服务器的方式相同)最初,这似乎是与 SQL Server 2019 中添加的新标量 UDF 内联功能相关的错误,因为您提到禁用内联解决了该问题。进一步检查后发现,由于函数定义中存在 CTE,因此无法内联该函数。
这是我重现该问题的(失败的)尝试:
USE [master];
GO
DROP DATABASE IF EXISTS [256861OtherDatabase];
GO
CREATE DATABASE [256861OtherDatabase];
GO
USE [256861OtherDatabase];
GO
CREATE TABLE dbo.TableForView2
(
ID int IDENTITY(1,1) NOT NULL,
DateComplete datetime NOT NULL,
SecondID int NOT NULL,
QuizID int NOT NULL,
CONSTRAINT PK_TableForView2 PRIMARY KEY (Id)
);
GO
CREATE TABLE dbo.TableForView3
(
ID int IDENTITY(1,1) NOT NULL,
Timeframe int NOT NULL,
QuizID int NOT NULL,
CONSTRAINT PK_TableForView3 PRIMARY KEY (Id)
);
GO
USE [master];
GO
DROP DATABASE IF EXISTS [256861];
GO
CREATE DATABASE [256861];
GO
USE [256861];
GO
CREATE TABLE dbo.TABLE1
(
ID int IDENTITY(1,1) NOT NULL,
PersonID int NOT NULL,
CONSTRAINT PK_TABLE1 PRIMARY KEY (Id)
);
GO
CREATE VIEW dbo.View2
AS
SELECT
ID,
DateComplete,
SecondID,
QuizID
FROM [256861OtherDatabase].dbo.TableForView2 d
GO
CREATE TABLE dbo.TABLE3
(
ID int IDENTITY(1,1) NOT NULL,
SecondID int NOT NULL,
[Name] varchar(50) NOT NULL,
CONSTRAINT PK_TABLE3 PRIMARY KEY (Id)
);
GO
CREATE VIEW dbo.View3
AS
SELECT
ID,
Timeframe,
QuizID
FROM [256861OtherDatabase].dbo.TableForView3 d
GO
CREATE FUNCTION dbo.TestFunction
(
@MyID INT = 150589,
@MyType VARCHAR(25) = 'Test'
)
RETURNS date
AS
BEGIN;
DECLARE @ExpirationDate DATE;
IF @MyType = 'Test'
BEGIN
DECLARE @Test1 INT;
WITH CTEuniqueName(ID, DateComplete)
AS (SELECT T1.PersonID, MAX(T2.DateComplete) AS DateComplete
FROM dbo.TABLE1 AS T1 WITH(NOLOCK)
LEFT JOIN dbo.VIEW2 AS T2 WITH(NOLOCK) ON T2.ID = T1.ID
WHERE T1.ID = @MyID
AND T2.SecondID IN(SELECT SecondID
FROM dbo.TABLE3 WITH(NOLOCK)
WHERE Name = 'TEST')
GROUP BY T1.PersonID)
SELECT @ExpirationDate = CASE
WHEN V3.TimeFrame > 0 THEN DATEADD(DAY, TimeFrame, CONVERT(DATE, CTE1.DateComplete))
ELSE NULL
END
FROM CTEuniqueName AS CTE1
INNER JOIN dbo.VIEW2 AS V2 WITH(NOLOCK) ON V2.ID = CTE1.ID
AND V2.DateComplete = CTE1.DateComplete
INNER JOIN dbo.VIEW3 AS V3 WITH(NOLOCK) ON V3.QuizID = V2.QuizID
WHERE V2.SecondID IN(SELECT SecondID
FROM dbo.TABLE3 WITH(NOLOCK)
WHERE Name = 'TEST');
END;
RETURN @ExpirationDate;
END;
GO
Run Code Online (Sandbox Code Playgroud)
我注意到该函数被标记为is_inlineable = 0,sys.sql_modules因此我再次运行函数创建代码,并运行以下扩展事件会话:
CREATE EVENT SESSION [inlining] ON SERVER
ADD EVENT sqlserver.tsql_scalar_udf_not_inlineable
ADD TARGET package0.event_file(SET filename=N'inlining')
WITH (STARTUP_STATE=OFF)
GO
Run Code Online (Sandbox Code Playgroud)
blocked_reason它生成了 1 个带有“CTE”的事件,该事件符合内联标量 UDF 要求(在该问题最初发布后已更新为包括 CTE)。
对我来说,这仍然是错误的行为。事实上,我唯一的建议是重写该函数以避免 CTE。这可能会解决该问题,并且还可以允许内联该函数。
| 归档时间: |
|
| 查看次数: |
1200 次 |
| 最近记录: |