加入收藏 | 设为首页 | 会员中心 | 我要投稿 核心网 (https://www.hxwgxz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程 > 正文

sql-server – 为什么TVP必须是READONLY,为什么其他类型的参数不

发布时间:2021-01-14 21:38:00 所属栏目:编程 来源:网络整理
导读:根据 this blog函数或存储过程的参数,如果它们不是OUTPUT参数,则基本上是按值传递,并且如果它们是OUTPUT参数,则基本上被视为更安全的传递版本. 起初我认为强制TVP被宣布为READONLY的目的是向开发人员明确表示TVP不能用作OUTPUT参数,但必须有更多进展因为我们

根据 this blog函数或存储过程的参数,如果它们不是OUTPUT参数,则基本上是按值传递,并且如果它们是OUTPUT参数,则基本上被视为更安全的传递版本.

起初我认为强制TVP被宣布为READONLY的目的是向开发人员明确表示TVP不能用作OUTPUT参数,但必须有更多进展因为我们不能将非TVP宣布为READONLY .例如,以下失败:

create procedure [dbo].[test]
@a int readonly
as
    select @a

Msg 346,Level 15,State 1,Procedure test

The parameter “@a” can not be declared READONLY since it is not a table-valued parameter.

>自TV001于statistics aren’t stored开始,防止DML操作背后的理由是什么?
>出于某种原因,它是否与不希望TVP成为OUTPUT参数有关?

解决方法

这个解释似乎与以下组合有关:a)来自链接博客的细节,这个问题中没有提到,b)TVP的语用符合参数总是进出的方式,c)和性质表变量.

>链接博客文章中包含的缺失细节正是变量如何传入和传出存储过程和函数(这与“如果它们是OUTPUT参数的更安全版本的传递参数”问题中的措辞有关“ ):

TSQL uses a copy-in/copy-out semantics to pass parameters to stored procedures and functions….

…when the stored proc finishes executing (without hitting an error) a copy-out is made which updates the parameter passed in with any changes that were made to it in the stored proc.

The real benefit of this approach is in the error case. If an error occurs in the middle of a stored procedure’s execution,any changes made to parameters will not propagate back to the caller.

If the OUTPUT keyword is not present no copy-out is made.

The bottom line:
Parameters to stored procs never reflect the partial execution of the stored proc if it encountered an error.

这个难题的第1部分是参数总是“按值”传递.并且,仅当参数标记为OUTPUT并且存储过程成功完成时才会实际发回当前值.如果OUTPUT值真正“通过引用”传递,那么指向该变量的内存中的位置的指针将是传递的东西,而不是值本身.如果您传入指针(即内存地址),则即使存储过程的下一行导致错误并且中止执行,也会立即反映所做的任何更改.

总结第1部分:始终复制变量值;他们的内存地址没有引用它们.
>考虑到第1部分,当传入的变量非常大时,始终复制变量值的策略可能会导致资源问题.我还没有测试过如何处理blob类型(VARCHAR(MAX),NVARCHAR(MAX),VARBINARY(MAX),XML以及那些不应该再使用的那些:TEXT,NTEXT和IMAGE),但它是安全的说任何传入的数据表都可能非常大.对于那些开发TVP功能的人来说,希望获得真正的“传递参考”能力以防止他们的酷炫新功能破坏健康数量的系统(即想要更具可扩展性的方法)是有意义的.正如你在the documentation中看到的那样,他们做了什么:

Transact-SQL passes table-valued parameters to routines by reference to avoid making a copy of the input data.

此外,这种内存管理问题并不是一个新概念,因为它可以在SQL Server 2005中引入的SQLCLR API中找到(TVP在SQL Server 2008中引入).将NVARCHAR和VARBINARY数据传递给SQLCLR代码(即SQLCLR程序集中.NET方法的输入参数)时,您可以选择使用SqlString或SqlBinary分别使用“by value”方法,或者可以选择分别使用SqlChars或SqlBytes的“按引用”方法. SqlChars和SqlBytes类型允许将数据完全流式传输到.NET CLR,这样您就可以提取大块的大值,而不是复制整个200 MB(最多2 GB,右)值.

总结第2部分:TVP,就其本质而言,如果留在“永远复制价值”模型中,就会倾向于消耗大量内存(从而恶化性能).因此,TVP做了一个真正的“通过参考传递”.
>最后一部分是为什么第2部分很重要:为什么真正“通过引用”传递TVP而不是制作它的副本改变任何东西.这可以通过作为第1部分基础的设计目标来解决:未成功完成的存储过程不应以任何方式改变任何输入参数,无论它们是否标记为OUTPUT.允许DML操作会立即影响TVP的值,因为它存在于调用上下文中(因为通过引用传递意味着您正在更改传入的内容,而不是传入的内容的副本).

现在,某个人,某个地方,可能正在与他们的监视器说话,“好吧,只需构建一个自动化设施,用于回滚对TVP参数所做的任何更改,如果有任何传入存储过程的话.杜.问题解决了.”没那么快.这就是表变量的性质所在:对表变量的更改不受事务约束!所以没有办法回滚变化.实际上,如果需要回滚,这是用于保存事务中生成的信息的技巧:-).

总结第3部分:表变量不允许在导致存储过程中止的错误的情况下“撤消”对它们所做的更改.这违反了设计目标,即让参数永远不会反映部分执行(第1部分).

Ergo:需要READONLY关键字来防止对TVP的DML操作,因为它们是实际通过“引用”传递的表变量,因此即使存储过程遇到错误,也会立即反映对它们的任何修改,并且别无他法.

此外,其他数据类型的参数不能使用READONLY,因为它们已经是传入内容的副本,因此它不会保护任何尚未受保护的内容.那个,以及其他数据类型的参数的工作方式是读写的,因此将API改为现在包含只读概念可能会更加有效.

(编辑:核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读