我运行data.bat文件与以下行:

Rem Tis batch file will populate tables

cd\program files\Microsoft SQL Server\MSSQL
osql -U sa -P Password -d MyBusiness -i c:\data.sql

数据的内容。SQL文件是:

   insert Customers
            (CustomerID, CompanyName, Phone)
             Values('101','Southwinds','19126602729')

还有8个类似的行用于添加记录。

当我运行这个开始>运行> cmd > c:\data.bat,我得到这个错误信息:

1>2>3>4>5>....<1 row affected>
Msg 8152, Level 16, State 4, Server SP1001, Line 1
string or binary data would be truncated.

<1 row affected>

<1 row affected>

<1 row affected>

<1 row affected>

<1 row affected>

<1 row affected>

此外,我显然是一个新手,但级别#和状态#意味着什么,我如何查找错误消息,如上面的一个:8152?


当前回答

我有这个问题,尽管数据长度比字段长度短。 事实证明,问题在于有另一个日志表(用于审计跟踪),由主表上的触发器填充,其中的列大小也必须更改。

其他回答

2016/2017更新将向您显示坏值和列。

新的跟踪标志将把旧的错误替换为新的2628错误,并打印出出错的列和值。Traceflag 460在2016年和2017年的最新累积更新中可用:

https://support.microsoft.com/en-sg/help/4468101/optional-replacement-for-string-or-binary-data-would-be-truncated

只要确保在你安装了CU之后,你启用了跟踪标志,或者是全局的或者是永久的在服务器上:

...或使用DBCC TRACEON:

https://learn.microsoft.com/en-us/sql/t-sql/database-console-commands/dbcc-traceon-trace-flags-transact-sql?view=sql-server-ver15

如果有人在c#应用程序中遇到这个错误,我创建了一个简单的方法来查找违规字段:

获取要进行插入/更新的表中所有列的列宽。(我直接从数据库中得到这个信息。) 将列的宽度与我们试图插入/更新的值的宽度进行比较。

假设/限制:

数据库中表的列名与c#实体字段匹配。例如:如果你在数据库中有这样一列: 你需要让你的实体具有相同的列名: 公共类SomeTable { //其他字段 公共字符串SourceData{获取;设置;} } 每次插入/更新一个实体。在下面的演示代码中会更清楚。(如果您正在进行批量插入/更新,您可能需要修改它或使用其他解决方案。)

步骤1:

直接从数据库中获取所有列的列宽:

// For this, I took help from Microsoft docs website:
// https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlconnection.getschema?view=netframework-4.7.2#System_Data_SqlClient_SqlConnection_GetSchema_System_String_System_String___
private static Dictionary<string, int> GetColumnSizesOfTableFromDatabase(string tableName, string connectionString)
{
    var columnSizes = new Dictionary<string, int>();
            
    using (var connection = new SqlConnection(connectionString))
    {
        // Connect to the database then retrieve the schema information.  
        connection.Open();

        // You can specify the Catalog, Schema, Table Name, Column Name to get the specified column(s).
        // You can use four restrictions for Column, so you should create a 4 members array.
        String[] columnRestrictions = new String[4];

        // For the array, 0-member represents Catalog; 1-member represents Schema;
        // 2-member represents Table Name; 3-member represents Column Name.
        // Now we specify the Table_Name and Column_Name of the columns what we want to get schema information.
        columnRestrictions[2] = tableName;

        DataTable allColumnsSchemaTable = connection.GetSchema("Columns", columnRestrictions);

        foreach (DataRow row in allColumnsSchemaTable.Rows)
        {
            var columnName = row.Field<string>("COLUMN_NAME");
            //var dataType = row.Field<string>("DATA_TYPE");
            var characterMaxLength = row.Field<int?>("CHARACTER_MAXIMUM_LENGTH");

            // I'm only capturing columns whose Datatype is "varchar" or "char", i.e. their CHARACTER_MAXIMUM_LENGTH won't be null.
            if(characterMaxLength != null)
            {
                columnSizes.Add(columnName, characterMaxLength.Value);
            }
        }

        connection.Close();
    }

    return columnSizes;
}

步骤2:

将列的宽度与我们试图插入/更新的值的宽度进行比较:

public static Dictionary<string, string> FindLongBinaryOrStringFields<T>(T entity, string connectionString)
{
    var tableName = typeof(T).Name;
    Dictionary<string, string> longFields = new Dictionary<string, string>();
    var objectProperties = GetProperties(entity);
    //var fieldNames = objectProperties.Select(p => p.Name).ToList();

    var actualDatabaseColumnSizes = GetColumnSizesOfTableFromDatabase(tableName, connectionString);
            
    foreach (var dbColumn in actualDatabaseColumnSizes)
    {
        var maxLengthOfThisColumn = dbColumn.Value;
        var currentValueOfThisField = objectProperties.Where(f => f.Name == dbColumn.Key).First()?.GetValue(entity, null)?.ToString();

        if (!string.IsNullOrEmpty(currentValueOfThisField) && currentValueOfThisField.Length > maxLengthOfThisColumn)
        {
            longFields.Add(dbColumn.Key, $"'{dbColumn.Key}' column cannot take the value of '{currentValueOfThisField}' because the max length it can take is {maxLengthOfThisColumn}.");
        }
    }

    return longFields;
}

public static List<PropertyInfo> GetProperties<T>(T entity)
{
    //The DeclaredOnly flag makes sure you only get properties of the object, not from the classes it derives from.
    var properties = entity.GetType()
                            .GetProperties(System.Reflection.BindingFlags.Public
                            | System.Reflection.BindingFlags.Instance
                            | System.Reflection.BindingFlags.DeclaredOnly)
                            .ToList();

    return properties;
}

演示:

假设我们试图插入SomeTable类的someTableEntity,它在我们的应用中是这样建模的:

public class SomeTable
{
    [Key]
    public long TicketID { get; set; }
    public string SourceData { get; set; }
}

它在我们的SomeDbContext中,像这样:

public class SomeDbContext : DbContext
{
    public DbSet<SomeTable> SomeTables { get; set; }
}

Db中的表SourceData字段为varchar(16),如下所示:

现在,我们将尝试在这个字段中插入一个长度超过16个字符的值,并捕获以下信息:

public void SaveSomeTableEntity()
{
    var connectionString = "server=SERVER_NAME;database=DB_NAME;User ID=SOME_ID;Password=SOME_PASSWORD;Connection Timeout=200";
        
    using (var context = new SomeDbContext(connectionString))
    {
        var someTableEntity = new SomeTable()
        {
            SourceData = "Blah-Blah-Blah-Blah-Blah-Blah"
        };
        
        context.SomeTables.Add(someTableEntity);
        
        try
        {
            context.SaveChanges();
        }
        catch (Exception ex)
        {
            if (ex.GetBaseException().Message == "String or binary data would be truncated.\r\nThe statement has been terminated.")
            {
                var badFieldsReport = "";
                List<string> badFields = new List<string>();
                
                // YOU GOT YOUR FIELDS RIGHT HERE:
                var longFields = FindLongBinaryOrStringFields(someTableEntity, connectionString);

                foreach (var longField in longFields)
                {
                    badFields.Add(longField.Key);
                    badFieldsReport += longField.Value + "\n";
                }
            }
            else
                throw;
        }
    }
}

badFieldsReport会有这样的值:

“SourceData”列不能接受的值 "啥啥啥"因为它能承受的最大长度是 16.

来自@gmmastros的回答

当您看到消息....时

字符串或二进制数据将被截断

想想自己……字段不够大,容不下我的数据。

检查客户表的表结构。我认为您会发现一个或多个字段的长度不足以容纳您试图插入的数据。例如,如果Phone字段是varchar(8)字段,并且您尝试在其中放入11个字符,则会得到此错误。

在其中一个INSERT语句中,您试图将一个太长的字符串插入到字符串(varchar或nvarchar)列中。

如果仅仅通过查看脚本还不清楚哪个INSERT是违规者,那么可以计算错误消息之前出现的<1行受影响的>行。得到的数字加1就是陈述号。在您的例子中,似乎是第二个INSERT产生了错误。

这种错误可能发生的另一种情况是 SQL Server Management Studio。如果你的表中有"text"或"ntext"字段, 不管你要更新什么类型的字段(例如位或整数)。 似乎工作室不加载整个“ntext”字段,也更新所有字段,而不是修改的字段。 要解决这个问题,请从Management Studio的查询中排除“text”或“ntext”字段