【问题标题】:How to solve the "String or binary data would be truncated.\r\nThe statement has been terminated." error?如何解决“字符串或二进制数据将被截断。\r\n语句已终止。”错误?
【发布时间】:2017-01-11 07:05:34
【问题描述】:

我有一个更新一些记录的程序。当我执行它时,我得到以下异常

"字符串或二进制数据将被截断。\r\n语句已终止。"

我发现当参数长度大于变量长度时会发生这种情况。我再次检查更改大小。但是没有用。再次出现同样的异常。我该如何解决这个问题?请帮忙

这是我的更新代码

            bool isFinished = dba.update(desingnation, title, initials, surname, fullname, callingName, civilSatatus, natinality, nic, birthday, passport,
                                          hometp, mobiletp, province, district, division, electorate, gramaNiladhari, takafull, p_city,
                                          c_city, p_hno, c_hno, tokens_P, tokens_C, previousEmployeements, bank, branch, type, account, gender, educatinalQ, languageE, languageS, languageT, empNo, appNo);
            if (isFinished)
            {
                WebMsgBox.Show("Successfully Inserted!");
            }
            else
            {
                WebMsgBox.Show("Some Errors Occured");
            }
        }
        else
        {
            WebMsgBox.Show("Some feilds are not valid");
        }
    }
}              

这是向存储过程传递参数的代码

            try
            {
                using (SqlCommand cmd = new SqlCommand())
                {
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.Connection = connection;
                    cmd.CommandTimeout = 0;
                    cmd.Transaction = transactions;

                    /*=======================Update employee details================================*/
                    cmd.CommandText = "update_HS_HR_EMPLOYEE_AADM";

                    cmd.Parameters.Add("@appNo", SqlDbType.Int).Value = appNo;
                    cmd.Parameters.Add("@CALLING_NAME", SqlDbType.VarChar).Value = callingName;
                    cmd.Parameters.Add("@INITIALS", SqlDbType.VarChar).Value = initials;
                    cmd.Parameters.Add("@SURNAME", SqlDbType.VarChar).Value = surname;
                    cmd.Parameters.Add("@TITLE", SqlDbType.VarChar).Value = title;
                    cmd.Parameters.Add("@NAME", SqlDbType.VarChar).Value = fullname;
                    cmd.Parameters.Add("@FULLNAME", SqlDbType.VarChar).Value = fullname + " " + surname;
                    cmd.Parameters.Add("@NIC", SqlDbType.VarChar).Value = nic;
                    cmd.Parameters.Add("@BDY", SqlDbType.VarChar).Value = birthday;
                    cmd.Parameters.Add("@GENDER", SqlDbType.VarChar).Value = gender;
                    cmd.Parameters.Add("@NATIONALITY", SqlDbType.VarChar).Value = natinality;
                    cmd.Parameters.Add("@CIVILSTATUS", SqlDbType.VarChar).Value = civilSatatus;
                    cmd.Parameters.Add("@DESIGNATION", SqlDbType.VarChar).Value = desingnation;
                    cmd.Parameters.Add("@P_ADD1", SqlDbType.VarChar).Value = p_hno;
                    cmd.Parameters.Add("@P_ADD2", SqlDbType.VarChar).Value = tokens_P[0];

                    if (tokens_P.Length > 1)
                        cmd.Parameters.Add("@P_ADD3", SqlDbType.VarChar).Value = tokens_P[1];
                    else
                        cmd.Parameters.Add("@P_ADD3", SqlDbType.VarChar).Value = "";

                    cmd.Parameters.Add("@P_CITY", SqlDbType.VarChar).Value = p_city;
                    cmd.Parameters.Add("@TP_HOME", SqlDbType.VarChar).Value = hometp;
                    cmd.Parameters.Add("@TP_MOBILE", SqlDbType.VarChar).Value = mobiletp;
                    cmd.Parameters.Add("@PROVINCE", SqlDbType.VarChar).Value = province;
                    cmd.Parameters.Add("@DISTRICT", SqlDbType.VarChar).Value = district;
                    cmd.Parameters.Add("@C_ADD1", SqlDbType.VarChar).Value = c_hno;
                    cmd.Parameters.Add("@C_ADD2", SqlDbType.VarChar).Value = tokens_C[0];
                    cmd.Parameters.Add("@PER_GNDIV_CODE", SqlDbType.VarChar).Value = gramaNiladhari;
                    cmd.Parameters.Add("@PER_DSDIV_CODE", SqlDbType.VarChar).Value = division;
                    cmd.Parameters.Add("@TAKAFUL", SqlDbType.VarChar).Value = takafull;
                    cmd.Parameters.Add("@PASSPORT_NO", SqlDbType.VarChar).Value = passport;

                    if (tokens_C.Length > 1)
                        cmd.Parameters.Add("@C_ADD3", SqlDbType.VarChar).Value = tokens_C[1];
                    else
                        cmd.Parameters.Add("@C_ADD3", SqlDbType.VarChar).Value = "";

                    cmd.Parameters.Add("@C_CITY", SqlDbType.VarChar).Value = c_city;
                    cmd.Parameters.Add("@ELECTORATE", SqlDbType.VarChar).Value = electorate;

                    //int appNO = int.Parse((cmd.ExecuteScalar().ToString()));
                    cmd.ExecuteNonQuery();
                    cmd.Parameters.Clear();         


  }     
}         

这是存储过程

ALTER PROCEDURE [dbo].[update_HS_HR_EMPLOYEE_AADM] 
@appNo Int,
@CALLING_NAME VARCHAR(50),
@INITIALS VARCHAR(50),
@SURNAME VARCHAR(50),
@TITLE VARCHAR(50),
@NAME VARCHAR(50),
@FULLNAME VARCHAR(100),
@NIC VARCHAR(15),
@BDY VARCHAR(50),
@GENDER CHAR(1),
@NATIONALITY VARCHAR(50),
@CIVILSTATUS VARCHAR(50),
@DESIGNATION VARCHAR(50),
@P_ADD1 VARCHAR(50),
@P_ADD2 VARCHAR(50),
@P_ADD3 VARCHAR(50),
@P_CITY VARCHAR(50),
@TP_HOME VARCHAR(50),
@TP_MOBILE VARCHAR(50),
@PROVINCE VARCHAR(50),
@DISTRICT VARCHAR(50),
@C_ADD1 VARCHAR(50),
@C_ADD2 VARCHAR(50),
@C_ADD3 VARCHAR(50),
@C_CITY VARCHAR(50),
@ELECTORATE VARCHAR(50),
@PER_GNDIV_CODE VARCHAR(50),
@PER_DSDIV_CODE VARCHAR(50),
@TAKAFUL VARCHAR(50),
@PASSPORT_NO VARCHAR(50)

AS

BEGIN

update [HS_HR_EMPLOYEE_AADM]
SET
       [EMP_CALLING_NAME]=@CALLING_NAME
       ,[EMP_MIDDLE_INI]=@INITIALS
       ,[EMP_SURNAME]=@SURNAME
       ,[EMP_TITLE]=@TITLE
       ,[EMP_NAMES_BY_INI]=@NAME
       ,[EMP_FULLNAME]=@FULLNAME
       ,[EMP_NIC_NO]=@NIC
       ,[EMP_BIRTHDAY]=@BDY
       ,[EMP_GENDER]=@GENDER
       ,[NAT_CODE]=@NATIONALITY
       ,[EMP_MARITAL_STATUS]=@CIVILSTATUS
       ,[EMP_DATE_JOINED]=GETDATE()
       ,[EMP_CONFIRM_FLG]=0
       ,[CT_CODE]='000008'
       ,[DSG_CODE]=@DESIGNATION
       ,[CAT_CODE]='000001'
       ,[EMP_PER_ADDRESS1]=@P_ADD1
       ,[EMP_PER_ADDRESS2]=@P_ADD2
       ,[EMP_PER_ADDRESS3]=@P_ADD3
       ,[EMP_PER_CITY]=@P_CITY
       ,[EMP_PER_TELEPHONE]=@TP_HOME
       ,[EMP_PER_MOBILE]=@TP_MOBILE
       ,[EMP_PER_PROVINCE_CODE]=@PROVINCE
       ,[EMP_PER_DISTRICT_CODE]=@DISTRICT
       ,[EMP_TEM_ADDRESS1]=@C_ADD1
       ,[EMP_TEM_ADDRESS2]=@C_ADD2
       ,[EMP_PER_ELECTORATE_CODE]=@ELECTORATE
       ,[EMP_TEM_ADDRESS3]=@C_ADD3
       ,[EMP_TEM_CITY]=@C_CITY
       ,[EMP_PER_GNDIV_CODE]=@PER_GNDIV_CODE
       ,[EMP_PER_DSDIV_CODE]=@PER_DSDIV_CODE
       ,[EMP_PASSPORT_NO]=@TAKAFUL
       ,[EMP_TAK]=@PASSPORT_NO
       where App_no = @appNo

END

【问题讨论】:

  • 能否请您删除不相关的代码
  • @un-lucky 我已经删除了一些从文本框中获取数据的代码。请看一下

标签: c# asp.net sql-server stored-procedures


【解决方案1】:

在 SqlDBType.Varchar 中指定与存储过程中指定的大小匹配的 C# 代码中的 varchar 大小,例如。

cmd.Parameters.Add("@CALLING_NAME", SqlDbType.VarChar, 50).Value = callingName;

对应存储过程中的参数@CALLING_NAME VARCHAR(50)。 这样可以确保在传递给存储过程时不会超出大小。

如果没有为字符串参数指定长度,ADO.NET 会选取可能超过存储过程 VARCHAR 参数中指定的大小的任意长度值。

同时在前端确保文本框中输入的字符数不超过相应的参数大小。 这可以使用MaxLength 属性来完成,或者如果大小超过,则使用 JQuery/Javascript 向用户提示消息。

对其他参数执行此操作并检查。

【讨论】:

    【解决方案2】:

    指定的错误,"String or binary data would be truncated.\r\nThe statement has been terminated." 显示当您尝试插入大于指定列大小的值时,当我们查看给定的过程时,我们无法识别每列的大小,所以如果你用你给出的值交叉检查列的大小会更好。

    我可以说@GENDER 可能会导致类似的问题,因为它在过程中被定义为@GENDER CHAR(1),,但是您将一个字符串传递给该方法并作为SqlDbType.VarChar 传递,而不是您必须给出值作为字符。对于这个特定的领域

    【讨论】:

    • 比较每个字段和您传递的值并验证您传递的值是否正确
    【解决方案3】:

    String or binary data would be truncated 错误告诉您正在丢失数据。这个错误的一个恼人的事情是它没有告诉你问题与哪一列有关,并且在这种情况下(有很多列),它很难诊断。

    如果您有合适的 SQL Server 版本(请参阅下面的超链接页面),您可以turn on Trace Flag 460(这可能需要重新启动)告诉您问题与哪个表和列有关。

    如果没有,这是我的更多手动方法...之后有一些关于如何在不出现此错误的情况下静默截断参数的信息(这不好)。


    请注意,对于每一列,C# 变量中都有一个值、声明的参数类型(在 C# 代码和存储过程中)和表中列的大小(问题中缺少其定义 -这可以解释为什么还没有一个公认的答案)。对于所有列,所有这些最大长度和类型都需要绑定。你真的需要检查所有这些;但我们都喜欢捷径,所以...

    我查找哪些列存在问题的技巧是找到发生问题的场景,以便您可以轻松地重复它 - 这很容易做到,因为您对此方法进行了单元测试。现在修改存储过程以注释掉一半的列,然后重试。

    • 如果它有效,那么您知道未注释的列很好(对于这组特定的数据),问题出在被注释掉的列中,所以取消注释一半的行,然后重试。
    • 如果不起作用,则问题出在未注释的列上,因此请注释掉其余列的一半,然后重试。
    • 重复直到确定哪些列有问题。 我说“列”,因为虽然可能只有一列存在这个问题,但可能不止这些。
    • 现在将所有内容恢复到开始时的状态。

    现在您已经确定了哪些列存在问题,对照存储的 proc 参数定义、C# 参数定义和 C# 变量中的值检查表中每个列的定义。您可能需要一直跟踪到在用户界面中输入值的位置,并确保有适当的限制以防止值太大。


    作为奖励提示,我喜欢让我的参数大小与它们相关的列的类型和大小相对应的单元测试。我还有表示每个字符串字段的最大长度和数字字段的最大值的常量。这些常量针对数据库中的列进行了单元测试,并在限制用户在用户界面中给出的值时使用。它们也可以用于该方法的单元测试,以证明为每列插入最大可能值确实有效。

    但是,请注意,由于参数强制会发生静默截断,因此值得使您的 varcharnvarcharvarbinary 参数大于列大小:

    ​SQL server 会默默地强制你的值是参数的任何类型。比如……

    DECLARE @Varchar VARCHAR(8) = 'I will be truncated';
    DECLARE @Decimal92 DECIMAL(9,2) = 123.456;
    DECLARE @Int INT = 123.456;
    SELECT @Varchar, @Decimal92, @Int;
    

    将输出...

    I will b 123.46 123
    

    这可能会让人感到意外,因为 SQL 会抱怨这样的事情:

    BEGIN TRANSACTION;
    CREATE TABLE tbl_Test(MyColumn NVARCHAR(5) NOT NULL);
    INSERT INTO dbo.tbl_Test (MyColumn) VALUES (N'I will be truncated');
    ROLLBACK TRANSACTION;
    

    通过说String or binary data would be truncated. 而下面的代码并没有抱怨,默默地强制值并插入记录:

    BEGIN TRANSACTION;
    CREATE TABLE tbl_Test(MyColumn NVARCHAR(5) NOT NULL);
    DECLARE @MyColumn NVARCHAR(5)=N'I will be truncated'
    INSERT INTO dbo.tbl_Test (MyColumn) VALUES (@MyColumn);
    ROLLBACK TRANSACTION; 
    

    因此,如果您想了解发生的截断问题,您需要确保您的参数的容量大于它要进入的列。例如,如果我们只更改一个字符...

    BEGIN TRANSACTION;
    CREATE TABLE tbl_Test(MyColumn NVARCHAR(5) NOT NULL);
    DECLARE @MyColumn NVARCHAR(6)=N'I will be truncated'
    INSERT INTO dbo.tbl_Test (MyColumn) VALUES (@MyColumn);
    ROLLBACK TRANSACTION; 
    

    ...会给出截断错误。但是,请注意这不是一个完整的解决方案,因为如果我尝试使用不同的值...

    BEGIN TRANSACTION;
    CREATE TABLE tbl_Test(MyColumn NVARCHAR(5) NOT NULL);
    DECLARE @MyColumn NVARCHAR(6)=N'Never complain'
    INSERT INTO dbo.tbl_Test (MyColumn) VALUES (@MyColumn);
    ROLLBACK TRANSACTION;
    

    然后它会默默地强制,在空格之后截断,然后(因为它是一个 varchar)值删除了尾随空格,它会毫无怨言地插入。

    所以唯一可以确定的方法是让你的参数比它需要的大几个字符,因为可能不会连续有多个空格。您可以对所有内容都使用 VARCHAR(MAX),但有人担心这可能会影响性能。

    其中一个特别重要的地方是加密值。如果加密值被截断,则您无法解密它们。因此,您需要确保您的 VARBINARY 参数的大小大于相关列,以便您得到错误而不是插入截断的值。在这种情况下,我相信一个更大的字符就足够了,因为没有修剪 VARBINARY。好吧,显然 VarBinarys 会从末尾修剪尾随的“nul”(ASCII = 0)字符;但仅当 ANSI_PADDING 设置为 OFF,但 as Microsoft say 时,它应该始终设置为“ON”。本节还介绍了具有不同设置的不同字段类型会发生什么修剪。

    值得一提的是,SQL 甚至与它的执行方式不一致。如果我们用 DECIMAL 重试原始示例...

    BEGIN TRANSACTION;
    CREATE TABLE tbl_Test(MyColumn DECIMAL(9,2) NOT NULL);
    INSERT INTO dbo.tbl_Test (MyColumn) VALUES (123.456);
    SELECT * FROM tbl_Test
    ROLLBACK TRANSACTION;
    

    它不会抱怨值有太多小数位,它只是默默地强制它。如果我通过一个比列具有更多小数位的参数来执行此操作,情况也是如此......

    BEGIN TRANSACTION;
    CREATE TABLE tbl_Test(MyColumn DECIMAL(9,2) NOT NULL);
    DECLARE @MyColumn DECIMAL(9,3)=123.456
    SELECT @MyColumn
    INSERT INTO dbo.tbl_Test (MyColumn) VALUES (@MyColumn);
    SELECT * FROM tbl_Test
    ROLLBACK TRANSACTION;
    

    如果我给它一个太大的值......

    BEGIN TRANSACTION;
    CREATE TABLE tbl_Test(MyColumn DECIMAL(9,2) NOT NULL);
    INSERT INTO dbo.tbl_Test (MyColumn) VALUES (1234567890.456);
    SELECT * FROM tbl_Test
    ROLLBACK TRANSACTION; 
    

    然后它会抱怨Arithmetic overflow error converting numeric to data type numeric.(就像我先将该值放入参数中一样)。

    要提到的另一件事与参数强制和加密有关。想象一个场景,您有一个类型为 DECIMAL(9,2) 的 SQL 列和一个相同类型的参数,并且您从 C# 代码中给它一个点网“十进制”。如果您的代码中的“小数”有很多小数位,那么这种无声强制将有效地要求 SQL 为您进行舍入。这很好......直到您决定加密该列,因为现在您加密的值将比 SQL DECIMAL 列能够保存的值长得多,因此可能比您允许的要大(在您的 VARBINARY 长度)。在这种情况下,您需要确保在加密之前将该值四舍五入到正确的小数位数。

    关于参数的修剪尾随空格。它只是根据需要修剪...这表明它已经从参数中修剪了一个空格,但在 N 之后留下了剩余的 4 个。

    BEGIN TRANSACTION; 
    CREATE TABLE tbl_Test(MyColumn NVARCHAR(5) NOT NULL); 
    DECLARE @MyColumn NVARCHAR(6)=N'N     complain' 
    SELECT @MyColumn +'|',LEN(@MyColumn),DATALENGTH(@MyColumn) 
    INSERT INTO dbo.tbl_Test (MyColumn) VALUES (@MyColumn); 
    SELECT MyColumn +'|',LEN(MyColumn),DATALENGTH(MyColumn) FROM dbo.tbl_Test 
    ROLLBACK TRANSACTION; 
    

    关于此的另一个学习点...相同的概念适用于与表定义相关的用户定义表类型。

    这是一个演示该问题的示例脚本。请注意,类型的创建和删除必须在事务之外完成。

    CREATE TYPE dbo.MyTableType AS TABLE (MyColumn NVARCHAR(5) NOT NULL); 
    GO 
    BEGIN TRANSACTION; 
    DECLARE @MyColumn NVARCHAR(5)=N'I will be truncated' 
    DECLARE @MyTable AS dbo.MyTableType; 
    INSERT INTO @MyTable (MyColumn) VALUES (@MyColumn); 
    CREATE TABLE dbo.tbl_Test (MyColumn NVARCHAR(5) NOT NULL); 
    INSERT INTO dbo.tbl_Test SELECT MyColumn FROM @MyTable; 
    ROLLBACK TRANSACTION; 
    GO 
    DROP TYPE dbo.MyTableType; 
    

    【讨论】:

      猜你喜欢
      • 2018-03-28
      • 1970-01-01
      • 2013-03-19
      • 1970-01-01
      • 2011-07-28
      • 1970-01-01
      • 1970-01-01
      • 2012-01-31
      相关资源
      最近更新 更多