订阅初始化抛出错误“选项“INLINE=ON”对此函数无效”,这是不正确的

Oll*_*lie 6 sql-server transactional-replication sql-server-2019

设想\n我们正在将 SQL 资产升级到 SQL Server 2019,并已设置生产环境。

\n\n

来自:\nSQL2008R2 Standard/Windows Server 2008R2 - 主服务器/辅助服务器(Windows 故障转移群集)和报表服务器(事务复制)。

\n\n

至:\nSQL2019 Standard/Windows Server 2019 - 主服务器/辅助服务器(始终开启)和报表服务器(事务复制)

\n\n

2008R2 的备份已于 2019 年恢复,兼容性级别设置为最新。始终在线设置相对顺利,针对当前应用程序的初步测试未显示兼容性问题。

\n\n

问题纯粹在于设置事务复制。有 2 个数据库需要复制,每个数据库都有一个发布,主数据库作为发布者和分发者。

\n\n

问题\n在报告服务器上每个数据库的订阅初始化期间,它运行良好,直到到达创建函数并产生以下错误为止。

\n\n

第一个数据库:

\n\n
\n

消息:选项“INLINE=ON”对此功能无效。检查函数中 INLINE 选项支持的构造的文档。\n 命令文本:CREATE FUNCTION [dbo].[f_clienttels - 镜像复制 ce2d3663eb494f3589bd5000dad1bf1f](@ClientID [int])\n RETURNS varchar WITH INLINE = ON, EXECUTE AS CALLER\n AS \n BEGIN ......

\n
\n\n

第二个数据库:

\n\n
\n

消息:为语句“CREATE/ALTER FUNCTION”指定了无效选项。\n 关键字“with”附近的语法不正确。如果此语句是公用表表达式、xmlnamespaces 子句或更改跟踪上下文子句,则前一个语句必须以分号终止。\n \')\' 附近的语法不正确。\n 命令文本: CREATE FUNCTION [dbo] .[GetGroupAndDescendantGroupsSelective - 镜像复制 46f329d5eed444428f45b052f07c7ea8](@GroupId [int])\n 返回表 INLINE = ON\n AS\n RETURN (\n WITH GroupsCTE AS ( ........

\n
\n\n

它们都是不同的错误,但我相信两者都与“INLINE=ON”选项有关,这些函数中都不存在此选项,如果您从订阅中删除这些文章,我们的函数都不会显式使用内联选项它只是在下一个函数上给出相同的错误(如果函数以 CTE 开头,则为 CTE 错误;如果不是,则为 INLINE=ON 错误)。

\n\n

因此,复制似乎是在复制之前将“WITH INLINE = ON”插入到函数中,然后在添加时出错。

\n\n

我已将所有实例修补到最新的 CU4 更新 15.0.4033.1,在两台服务器上测试了这些功能(有效),验证了数据库中的所有功能/过程,一切都很好。在当前的 2008R2 环境中,我必须在几个月前重新创建该出版物,但没有收到这些错误。作为目前的解决方法,我正在订阅者处手动创建函数并从发布中删除所有函数文章。

\n\n

任何解决此问题的帮助将不胜感激,对此错误的唯一引用(来自第一个数据库)是提到它没有记录(下面的链接),我找不到任何其他论坛帖子提到它。

\n\n
\n

16203 \xe2\x80\x93 选项 \xe2\x80\x9cINLINE=ON\xe2\x80\x9d 对于该函数无效。检查函数中 INLINE 选项支持的构造的文档。

\n
\n\n

来自:Brent Ozar - SQL Server 2019\xe2\x80\x99s sys.messages 中的\xe2\x80\x99s 新增功能:更多未宣布的功能

\n\n

我们与 Microsoft 没有有效的支持合同,但正在尝试通过提供许可证的供应商联系他们,因此如果他们回复我们,我将在此处提供更新。

\n\n

如果需要,我可以提供更多信息。

\n

Oll*_*lie 3

对于延迟发布这些发现表示歉意,我花了一个多月的时间与微软支持人员就此事进行了反复沟通(有一次他们告诉我,复制并不是为了复制函数和存储过程而设计的,只是不将它们作为修复来复制?!!),在我向他们发送了我的实验证据后,他们最终承认这确实是一个错误。该错误实际上与 SQL Engine 中的数据库恢复/升级过程有关,而不是最初出现时的事务复制本身。

\n

原因/调查

\n

其根本原因是,当数据库从以前版本的 SQL(本例中为 2008R2)恢复到新的 SQL 2019 实例时,默认情况下,它似乎会在sys.sqlmodules 中标记带有inline_type = 1 的函数(这表明GUI 中函数属性中的“标记为内联”)。该表中有一个名为inlinable的标志,在最初调查时有点转移注意力,因为复制似乎使用inline_type代替,即使在解决方法之后, inlinable仍然为 true,并且不会妨碍复制。

\n

我通过以下步骤得出了这个结论:

\n
    \n
  • 从 2008R2 开始恢复数据库并将兼容性设置为 150:恢复了 100 多个具有 inline_type = 1 和 inlinable = 1 的函数。因此,当启动复制时,这些函数最终会失败,并显示...
  • \n
\n
\n

16203 \xe2\x80\x93 选项 \xe2\x80\x9cINLINE=ON\xe2\x80\x9d 对于该函数无效。

\n
\n

由于 inline_type 标志,复制会自动应用此选项。

\n

或者

\n
\n

为语句“CREATE/ALTER FUNCTION”指定了无效选项。关键字“with”附近的语法不正确。

\n
\n

第二个错误的原因是因为它在复制的代码中自动声明 INLINE = ON,导致当函数包含 CTE 时语法无效。

\n
    \n
  • 克隆恢复的数据库:结果与上面相同
  • \n
  • 将数据库兼容性级别设置为 140/130/120/110(2019 年预内联更改):结果与上面相同。
  • \n
  • 首先在复制订阅者上恢复和升级数据库:结果与上面相同。
  • \n
  • 手动设置 INLINE = OFF: 并不能解决问题,并且还会报告为某些函数的语法无效。
  • \n
  • ALTER DATABASE SCOPED CONFIGURATION SET TSQL_SCALAR_UDF_INLINING = Off:结果与上面相同。
  • \n
  • 将订阅创建为“拉取”而不是“推送”:结果与上面相同。
  • \n
  • 删除其中一个函数并重新创建:重新创建的函数成功,但在下一个函数上失败。
  • \n
  • 完全从生成的脚本重新创建数据库:复制时不会出现错误。
  • \n
\n

解决方法

\n

唯一可行的解​​决方法是在恢复/升级后重新创建所有函数,以便正确设置它们的标志。注意:并非所有函数都被错误标记,有些函数应该使用inline_type = 1 进行合法标记,解决方法将正确设置这些函数,因此如果它们被捕获在下面的脚本中也不会造成伤害。

\n

我通过快速而肮脏的PowerShell(如下)完成了此操作,一些类似问题的Microsoft文章建议sp_refreshsqlmodule这在第一个实例中对我不起作用,但我看不出任何不可行的逻辑原因,所以它值得一试首先尝试而不是这个。

\n
#Recreate all function to fix inlining issue\n#BACKUP DATABASE FIRST! Small Potential for dropped functions to not recreate!\n$SQLServer = "YOURSERVER\\Instance"\n$SQLDatabase = "YourDatabase"\n$Constring = "server='$SQLServer';database='$SQLDatabase';trusted_connection=true;"\n$GetFunctionsSQL = "SELECT m.definition,o.name  FROM sys.sql_modules m INNER JOIN \nsys.objects o ON m.object_id = o.object_id WHERE m.inline_type = 1 AND o.type IN ('FN','IF','TF')"\n$con = New-Object "System.Data.SqlClient.SqlConnection"\n$con.ConnectionString = $Constring\n$cmd = New-Object "System.Data.SqlClient.SqlCommand"\n$cmd.Connection = $con\n$cmd.CommandText = $GetFunctionsSQL\n$sda = New-Object "System.Data.SqlClient.SqlDataAdapter"\n$sda.SelectCommand = $cmd\n$dt = New-Object System.Data.Datatable\nTry {\n   $sda.Fill($dt)    >> $null\n}\ncatch {\n   Write-Error "$_"\n}\nforeach ($r in $dt.Rows) {\n   $nme = $r["name"]\n   $def = $r["definition"]\n   Write-Host "Processing $nme"\n   $con = New-Object "System.Data.SqlClient.SqlConnection"\n   $con.ConnectionString = $Constring\n   $cmd = New-Object "System.Data.SqlClient.SqlCommand"\n   $cmd.Connection = $con\n   $cmd.CommandText = "drop function $nme"\n   Try {\n      $cmd.Connection.Open()\n      $res = $cmd.ExecuteNonQuery()\n   }\n   catch {\n      Write-Error "$_"\n   }\n   if ($res -eq "-1") {\n      Write-Host "Reacreating..."\n      $cmd.Connection.Close()\n      $cmd.Connection = $con\n      $cmd.CommandText = "$def"\n      Try {\n         $cmd.Connection.Open()\n         $res = $cmd.ExecuteNonQuery()\n      }\n      catch {\n         Write-Error "$_"\n      }\n      $cmd.Connection.Close()\n   }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

最终决议

\n

必须在 SQL 引擎/数据库恢复代码中修复根本原因,才能真正解决这种情况。Microsoft 已确认将在 SQL Server 2019 CU6 更新中实施此修复。

\n
\n

最后,我们能够针对 CU6 中出现的此问题提出修补程序请求。这可能会在 2020 年 7 月或 8 月发布。

\n
\n

不确定我是否同意将其称为修补程序,因为您必须等待 3 个月!

\n

希望这对其他人有帮助,也可以作为一个教训,当您联系 Microsoft 支持时,准备好自行进行大量调查并将其发送给他们,这确实有助于加快流程。

\n