在 MS SQL Server 中正确使用 varbinary 类型

bbq*_*bot 4 sql-server t-sql parameter array table-valued-parameters

我在一个新职位上,被告知要实现一个存储过程,该过程将接受用户 ID 列表并更新一个标志。

当我建议使用表值参数(数组模拟)时,我被告知要实现如下所示,因为这是以前的做法。我不是 DBA,但我是一名全栈开发人员,这对我来说很有趣。

仅供参考 - 用户 ID 实现为类型 int

CREATE PROCEDURE [dbo].[UpdateUsers]       
 @rgIDs varbinary(max)      -- contains several ids
AS      
 DECLARE @tblTmp TABLE (ID int PRIMARY KEY)      

 DECLARE @ich int, @cch int, @ID int      
 SET @cch = DATALENGTH (@rgIDs)      
 SET @ich = 1   

 WHILE (@ich < @cch)      
 BEGIN      
  SET @ID = SUBSTRING (@rgIDs, @ich, 4) 

  UPDATE dbo.Users u 
  SET isUpdated = 1
  WHERE u.ID = @ID

  SET @ich = @ich + 4      
 END      
Run Code Online (Sandbox Code Playgroud)

不是表值类型在这里工作得更好吗?更具可读性、性能、更不容易出错等......?

我相信我将使用 SQL Server 2012 或 2014。绝对> 2008年。

Sol*_*zky 9

我被告知要按照下面的演示进行实施,因为这是以前的做法。......这对我来说很有趣。

你知道什么比这种方法更有趣吗?那条“推理”。旧的“这就是它一直以来的做法”只是一种避免思考和讨论它的方法。即使发现代码是最好的方法,仅此一项就应该引起危险。之前有人告诉我同样的事情,这是出于同样可悲的潜在原因:采用这种方法是出于过时的原因(例如旧版本的 SQL Server 没有特定功能,但我们使用的是具有该功能的较新版本)。非常令人沮丧。

不是表值类型在这里工作得更好吗?更具可读性、性能、不易出错等...

假设您使用的是 SQL Server 2008 或更高版本,那么答案通常都是肯定的。我的意思是,当前的代码是一种避免字符串拆分操作的有点聪明的方法。但是,TVP 可以是强类型的,因此无需int将应用程序层中的值修改为byte[]just 以便它可以在WHILE此处循环解压缩。基于该列表中的 ID 数量(当前代码创建和提交)每个语句1 个事务,因为循环未包含在显式事务中,因此单个基于集合的UPDATE语句的性能将比 N 个语句好得多. 因此,在当前方法中,要更新的 20 个 ID = 20 个事务。这就是为什么单一的、基于集合的UPDATEUPDATEWHILEUPDATE 效率更高。

只要确保实现全流方法,这意味着:不要将集合中的值转储到DataTable! 不幸的是,您在大多数示例中都会发现这样做是对时间、内存和 CPU 的不必要浪费。相反,创建一个实现IEnumerable、接受 UserID 集合并返回 的方法IEnumerable<SqlDataRecord>。然后,使用该方法作为SqlParameterTVP的“值” 。在该方法中,循环遍历集合,并对每个元素调用yield return;.

在我对以下问题的回答中,我有一些关于 StackOverflow 的示例代码:

将字典传递给存储过程 T-SQL

请注意,我听说,虽然尚未测试,但在更新的 SQL Server 版本(2014 或更新版本)上,通过对用户定义的表类型使用内存中 OLTP 可能会获得额外的性能提升。


此外,由于您提到这一点是希望“更易读的代码”的一部分,我建议使用有意义的参数和变量名称:-)。是的,我确实意识到这可能不是您的代码,但只是想我会提到它。此外,我会摆脱DECLARE @tblTmp它,因为它没有被使用,但同样,我意识到这可能是您编辑的更大示例的一部分。