SQL Server以静默方式截断存储过程中的varchar

根据这个论坛讨论 ,SQL Server(我正在使用2005年,但我收集这也适用于2000年和2008年)静默截断任何指定为存储过程参数的varchar到varchar的长度,即使插入该string直接使用INSERT实际上会导致错误。 例如。 如果我创build这个表格:

 CREATE TABLE testTable( [testStringField] [nvarchar](5) NOT NULL ) 

那么当我执行以下:

 INSERT INTO testTable(testStringField) VALUES(N'string which is too long') 

我收到一个错误:

 String or binary data would be truncated. The statement has been terminated. 

大。 数据完整性保存,主叫方知道。 现在让我们定义一个存储过程来插入:

 CREATE PROCEDURE spTestTableInsert @testStringField [nvarchar](5) AS INSERT INTO testTable(testStringField) VALUES(@testStringField) GO 

并执行它:

 EXEC spTestTableInsert @testStringField = N'string which is too long' 

没有错误,1行受到影响。 一行被插入到表中, testStringField为'strin'。 SQL Server以静默方式截断存储过程的varchar参数。

现在,这种行为可能有时候会很方便,但是我收集的方法是没有办法关掉它。 这是非常烦人的,因为我如果我传递太长的string存储过程错误的东西。 似乎有两种方法来处理这个问题。

首先,声明存储的proc的@testStringField参数为6,并检查它的长度是否超过5.这看起来有些黑客,并涉及刺激性的大量的样板代码。

其次,声明所有存储过程的varchar参数为varchar(max) ,然后让存储过程中的INSERT语句失败。

后者似乎工作正常,所以我的问题是:是否是一个好主意,使用SQL Server存储过程中的stringALTERS ALWAYS,如果我真的希望存储的过程失败,当一个string传递太长时间? 它甚至可能是最佳实践吗? 不能被禁用的无声截断对我来说似乎是愚蠢的。

它只是。

我从来没有注意到一个问题,因为我的一个检查将确保我的参数匹配我的表列长度。 在客户端代码也是如此。 就个人而言,我希望SQL永远不会看到太长的数据。 如果我看到截断的数据,那么会发生什么造成的。

如果您确实需要varchar(max),那么会由于数据types优先级而导致严重的性能问题。 varchar(max)的优先级高于varchar(n)(最长的是最高的)。 因此,在这种types的查询中,您将得到一个扫描而不是查询,并且每个varchar(100)值是CAST到varchar(max)

 UPDATE ...WHERE varchar100column = @varcharmaxvalue 

编辑:

有一个开放的Microsoft Connect项目关于这个问题。

在Erland Sommarkog的严格设置 (和匹配的连接项目 )中,这可能是值得的。

编辑2,Martins评论之后:

 DECLARE @sql VARCHAR(MAX), @nsql nVARCHAR(MAX); SELECT @sql = 'B', @nsql = 'B'; SELECT LEN(@sql), LEN(@nsql), DATALENGTH(@sql), DATALENGTH(@nsql) ; DECLARE @t table(c varchar(8000)); INSERT INTO @t values (replicate('A', 7500)); SELECT LEN(c) from @t; SELECT LEN(@sql + c), LEN(@nsql + c), DATALENGTH(@sql + c), DATALENGTH(@nsql + c) FROM @t; 

谢谢,一如既往的StackOverflow引发这种深入的讨论。 我最近一直在研究存储过程,使用标准的方法来处理事务和try / catch块,使它们更加健壮。 我不同意Joe Stefanelli的说法,“我的build议是让应用程序的一方负责”,并完全同意Jez的观点:“让SQL Servervalidationstring的长度会更可取”。 对我来说,使用存储过程的重点在于,它们是用数据库本地语言编写的,应该作为最后一道防线。 在应用程序方面,255和256之间的差异只是一个没有数字的数字,但在数据库环境中,最大大小为255的字段将不能接受256个字符。 应用程序validation机制应该尽可能反映后端数据库,但是维护很困难,所以如果应用程序错误地允许不适合的数据,我希望数据库给予我很好的反馈。 这就是为什么我使用数据库,而不是一堆CSV或JSON的文本文件或其他。

我很困惑,为什么我的一个SP扔了8152错误,另一个默默截断。 我终于扭了一下:抛出8152错误的SP有一个参数,它允许比相关表列多一个字符。 表列被设置为nvarchar(255),但参数是nvarchar(256)。 那么,我的“错误”是不是会解决gbn的担忧:“大规模的性能问题”? 也许我们可以一直将表列的大小设置为255,将SP参数设置为只有一个字符,比如说256,这样就解决了无声截断问题,并且不会导致任何性能损失。 据推测,还有一些我没有想到的缺点,但对我来说似乎是一个很好的妥协。

更新:恐怕这种技术不一致。 进一步的testing表明,我有时可能会触发8152错误,有时数据被无声地截断。 如果有人能帮我find一个更可靠的方法来处理这个问题,我将不胜感激。

更新2:请参阅Pyitoechito在此页上的答案。

同样的行为可以在这里看到:

 declare @testStringField [nvarchar](5) set @testStringField = N'string which is too long' select @testStringField 

我的build议是在调用存储过程之前使应用程序端负责validationinput。

更新:恐怕这种技术不一致。 进一步的testing表明,我有时可能会触发8152错误,有时数据会被无声地截断。 如果有人能帮我find一个更可靠的方法来处理这个问题,我将不胜感激。

这可能是因为string中的第256个字符是空白的。 VARCHAR会在插入时截断尾随的空格,并生成一个警告。 因此,您的存储过程悄悄地将您的string截断为256个字符,并且您的插入将截断末尾的空格(带有警告)。 当所说的字符不是空格时会产生一个错误。

也许一个解决scheme是使存储过程的VARCHAR长度适合捕捉非空白字符。 VARCHAR(512)可能会足够安全。

一个解决scheme是:

  1. 将所有传入参数更改为varchar(max)
  2. 有正确的数据长度的SP专用variables(只需复制和粘贴所有参数,并在最后添加“int”
  3. 声明一个表variables,列名与variables名相同
  4. 在表格中插入一行,其中每个variables以相同的名称进入列
  5. 从表中select内部variables

这样,您对现有代码的修改将会非常小,如下面的示例所示。

这是原始的代码:

 create procedure spTest ( @p1 varchar(2), @p2 varchar(3) ) 

这是新的代码:

 create procedure spTest ( @p1 varchar(max), @p2 varchar(max) ) declare @p1Int varchar(2), @p2Int varchar(3) declare @test table (p1 varchar(2), p2 varchar(3) insert into @test (p1,p2) varlues (@p1, @p2) select @p1Int=p1, @p2Int=p2 from @test 

请注意,如果传入参数的长度将大于限制,而不是悄悄地切断string,则SQL Server将引发错误。

你总是可以把一个if语句放到你的sp中去检查它们的长度,如果它们大于指定的长度则会抛出一个错误。 虽然这是相当耗时的,如果更新数据大小,更新会很痛苦。