根据this forum discussion,SQL Server(我使用的是2005,但我认为这也适用于2000和2008)会悄悄地将指定为存储过程参数的任何varchar截断为varchar的长度,即使直接使用INSERT插入该字符串实际上会导致错误.如果我创建这个表:

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参数.

现在,这种行为有时可能很方便,但我想没有办法关闭它.这是非常烦人的,因为如果我向存储过程传递太长的字符串,我会出错.似乎有两种方法可以解决这个问题.

首先,将存储的proc的@testStringField参数声明为大小6,并判断其长度是否超过5.这看起来有点像黑客,涉及大量令人恼火的样板代码.

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

后者似乎工作得很好,所以我的问题是:如果我真的希望在传递的字符串太长时存储过程失败,那么在SQL Server存储过程中始终对字符串使用varchar(max)是一个好主意吗?这可能是最佳实践吗?在我看来,无法禁用的silent截断似乎很愚蠢.

推荐答案

只有is英镑.

但我从未注意到问题,因为我的判断之一是确保参数与表列长度匹配.在客户端代码中也是如此.就我个人而言,我希望SQL永远不会看到太长的数据.如果我真的看到了被截断的数据,很明显是什么导致了它.

如果你真的觉得有必要使用varchar(max),那就要小心一个巨大的性能问题,因为它有datatype precedence个.varchar(max)的优先级高于varchar(n)(最长为最高).因此,在这种类型的查询中,您将得到一个扫描而不是搜索,并且每个varchar(datatype precedence)值都被转换为varchar(max)

UPDATE ...WHERE varchar100column = @varcharmaxvalue

编辑:

关于这个问题有open Microsoft Connect item个答案.

它可能值得列入Erland Sommarkog's Strict settings(和matching Connect item).

编辑2,在Martins comments 之后:

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;

Sql相关问答推荐

Microsoft Access UNION将长文本字段限制为255个字符

PG SQL中按条件聚合值

在SQL中将相同且紧挨着的元素进行分组

BigQuery-当子查询不返回任何结果时,所有结果为零

在Postgres中合并相似的表

仅在日期相隔时递增(Oracle SQL)

两个月之间的WHERE CASE WHEN-ORA-00905:缺少关键字

聚合内部的条件在哪里?

连续天跟踪购买情况(将标记返回到另一列?)

DB2 SQL查询结果多余数据

使用多个数据库调用重载 CQRS 模式

如何在第二个 INSERT 中使用第一个 INSERT 自动生成的 ID

在where语句中使用CTE非常缓慢

SQL中如何转置表格 UNPIVOT是唯一的 Select 吗?

标量子查询中的窗口函数不起作用

如何使用Informix创建一个临时表,将数据从根表导入并使用筛选条件

自动生成计算频率的列

如何显示最常引用条目的详细信息

根据是否存在值组合分组并 Select 行

根据开始/结束标记将 GROUP_ID 分配给行