ได้รับความยาวคอลัมน์ที่ไม่ถูกต้องจากไคลเอนต์ bcp สำหรับ colid 6


87

ฉันต้องการอัปโหลดข้อมูลไฟล์ csv จำนวนมากไปยังเซิร์ฟเวอร์ sql 2005 จากรหัส c # แต่ฉันพบข้อผิดพลาดด้านล่าง -

ได้รับความยาวคอลัมน์ที่ไม่ถูกต้องจากไคลเอนต์ bcp สำหรับ colid 6

เมื่อสำเนาจำนวนมากเขียนไปยังเซิร์ฟเวอร์ฐานข้อมูล

คำตอบ:


69

คอลัมน์ข้อมูลหนึ่งใน excel (Column Id 6) มีข้อมูลเซลล์อย่างน้อยหนึ่งคอลัมน์ที่เกินความยาวประเภทข้อมูลคอลัมน์ข้อมูลในฐานข้อมูล

ตรวจสอบข้อมูลใน excel ตรวจสอบข้อมูลใน excel ด้วยว่ารูปแบบเป็นไปตามสคีมาตารางฐานข้อมูล

เพื่อหลีกเลี่ยงปัญหานี้ให้ลองเกินความยาวข้อมูลของประเภทข้อมูลสตริงในตารางฐานข้อมูล

หวังว่านี่จะช่วยได้


1
โดยเฉพาะอย่างยิ่งหากคุณมีคอลัมน์ใด ๆ ที่ VARCHAR เล็กกว่า 4 โปรดระวัง NULL ในข้อมูลของคุณจะตีความผิดเป็นสตริง 4char "NULL"
CrazyPyro

196

ฉันรู้ว่าโพสต์นี้เก่า แต่ฉันพบปัญหาเดียวกันนี้และในที่สุดก็หาวิธีแก้ไขเพื่อระบุว่าคอลัมน์ใดเป็นสาเหตุของปัญหาและรายงานกลับตามต้องการ ฉันพิจารณาแล้วว่าสิ่งที่colidส่งคืนใน SqlException ไม่ใช่ศูนย์ดังนั้นคุณต้องลบ 1 ออกเพื่อให้ได้ค่า หลังจากนั้นจะใช้เป็นดัชนีของ_sortedColumnMappingsArrayList ของอินสแตนซ์ SqlBulkCopy ไม่ใช่ดัชนีของการแมปคอลัมน์ที่ถูกเพิ่มลงในอินสแตนซ์ SqlBulkCopy สิ่งหนึ่งที่ควรทราบก็คือ SqlBulkCopy จะหยุดข้อผิดพลาดแรกที่ได้รับดังนั้นนี่อาจไม่ใช่ปัญหาเดียว แต่อย่างน้อยก็ช่วยในการคิดออก

try
{
    bulkCopy.WriteToServer(importTable);
    sqlTran.Commit();
}    
catch (SqlException ex)
{
    if (ex.Message.Contains("Received an invalid column length from the bcp client for colid"))
    {
        string pattern = @"\d+";
        Match match = Regex.Match(ex.Message.ToString(), pattern);
        var index = Convert.ToInt32(match.Value) -1;

        FieldInfo fi = typeof(SqlBulkCopy).GetField("_sortedColumnMappings", BindingFlags.NonPublic | BindingFlags.Instance);
        var sortedColumns = fi.GetValue(bulkCopy);
        var items = (Object[])sortedColumns.GetType().GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(sortedColumns);

        FieldInfo itemdata = items[index].GetType().GetField("_metadata", BindingFlags.NonPublic | BindingFlags.Instance);
        var metadata = itemdata.GetValue(items[index]);

        var column = metadata.GetType().GetField("column", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(metadata);
        var length = metadata.GetType().GetField("length", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).GetValue(metadata);
        throw new DataFormatException(String.Format("Column: {0} contains data with a length greater than: {1}", column, length));
    }

    throw;
}

4
วิธีนี้ใช้งานได้ดีมากขอขอบคุณสำหรับการส่ง
Steven

1
คุณรู้หรือไม่ว่าเป็นไปได้ที่จะได้รับแถว nr ด้วยหรือไม่?
Gerhard Powell

2
DataFormatException เป็นข้อยกเว้นที่กำหนดเองดังนั้นฉันจึงสามารถรายงานปัญหากลับเนื่องจากความยาวคอลัมน์ไม่ถูกต้อง ฉันยังไม่สามารถหาวิธีรับหมายเลขแถวได้
b_stil

1
ทางออกที่ดีสิ่งเดียวที่ขาดหายไปคือในการจับ sqlTran.RollBack ();
pqsk

8
อาจจะเห็นได้ชัดสำหรับคนส่วนใหญ่ แต่"colid ที่ส่งคืนใน SqlException ไม่ได้เป็นศูนย์"ช่วยให้ฉันกำจัดสิ่งนี้ออกไปได้
ขอทาน

4

ฉันประสบปัญหาคล้าย ๆ กันขณะส่งสตริงไปยังตารางฐานข้อมูลโดยใช้ตัวเลือก SQL BulkCopy สตริงฉันถูกส่งผ่านเป็น 3 varchar(20)ตัวอักษรในขณะที่ระยะเวลาในคอลัมน์ปลายทางคือ ฉันลองตัดแต่งสตริงก่อนที่จะแทรกลงในฐานข้อมูลโดยใช้Trim()ฟังก์ชันเพื่อตรวจสอบว่าปัญหาเกิดจากการเว้นวรรค (นำหน้าและต่อท้าย) ในสตริงหรือไม่ หลังจากตัดแต่งสตริงแล้วก็ใช้งานได้ดี

คุณสามารถลอง text.Trim()


เยี่ยมมากขอบคุณมาก! นั่นเป็นเหตุผลที่แน่นอนในกรณีของฉันเช่นกัน - ช่องว่างต่อท้ายและความยาวคอลัมน์ผลลัพธ์เกิน
aleor

ถ้าสตริงอาจเป็นโมฆะให้ใช้ text? .rim ()
Charles Plager

1

ตรวจสอบขนาดของคอลัมน์ในตารางที่คุณกำลังทำการแทรก / คัดลอกจำนวนมาก อาจต้องขยาย varchar หรือคอลัมน์สตริงอื่น ๆ หรือค่าที่คุณกำลังแทรกจะต้องถูกตัดแต่ง ลำดับคอลัมน์ควรจะเหมือนกับในตาราง

เช่นเพิ่มขนาดของคอลัมน์ varchar 30 ถึง 50 =>

แก้ไขตาราง [dbo] [TableName] แก้ไขคอลัมน์ [ColumnName] Varchar (50)


0

รหัสชิ้นเยี่ยมขอบคุณสำหรับการแบ่งปัน!

ฉันลงเอยด้วยการใช้การสะท้อนกลับเพื่อรับ DataMemberName จริงเพื่อส่งกลับไปยังไคลเอนต์เมื่อเกิดข้อผิดพลาด (ฉันใช้การบันทึกจำนวนมากในบริการ WCF) หวังว่าคนอื่นจะพบว่าฉันมีประโยชน์อย่างไร

static string GetDataMemberName(string colName, object t) {
  foreach(PropertyInfo propertyInfo in t.GetType().GetProperties()) {
    if (propertyInfo.CanRead) {
      if (propertyInfo.Name == colName) {
        var attributes = propertyInfo.GetCustomAttributes(typeof(DataMemberAttribute), false).FirstOrDefault() as DataMemberAttribute;
        if (attributes != null && !string.IsNullOrEmpty(attributes.Name))
          return attributes.Name;
        return colName;
      }
    }
  }
  return colName;
}


วิธีนี้สามารถนำไปใช้?
Apollo

0

ฉันได้รับข้อความแสดงข้อผิดพลาดนี้พร้อมเวอร์ชัน ssis ที่ใหม่กว่ามาก (เทียบกับองค์กรปี 2015 ฉันคิดว่าเป็น ssis 2016) ฉันจะแสดงความคิดเห็นที่นี่เพราะนี่เป็นการอ้างอิงครั้งแรกที่เกิดขึ้นเมื่อคุณ Google ข้อความแสดงข้อผิดพลาดนี้ ฉันคิดว่าส่วนใหญ่เกิดขึ้นกับคอลัมน์อักขระเมื่อขนาดอักขระต้นทางใหญ่กว่าขนาดอักขระเป้าหมาย ฉันได้รับข้อความนี้เมื่อฉันใช้การป้อนข้อมูล adm.net ไปยัง ms sql จากฐานข้อมูล teradata ตลกเพราะ oledb ก่อนหน้านี้เขียนถึง ms sql จัดการการแปลงอักขระทั้งหมดได้อย่างสมบูรณ์แบบโดยไม่มีการแทนที่การเข้ารหัส หมายเลข colid และคอลัมน์การป้อนข้อมูลปลายทางที่เกี่ยวข้อง # บางครั้งคุณได้รับพร้อมกับข้อความ colid นั้นไร้ค่า ไม่ใช่คอลัมน์เมื่อคุณนับถอยหลังจากด้านบนของการทำแผนที่หรืออะไรทำนองนั้น ถ้าฉันเป็นไมโครซอฟท์ฉัน ' d ต้องอายที่จะให้ข้อความแสดงข้อผิดพลาดที่ดูเหมือนว่ากำลังชี้ไปที่คอลัมน์ปัญหาเมื่อไม่เป็นเช่นนั้น ฉันพบปัญหา colid โดยทำการเดาอย่างมีความรู้จากนั้นเปลี่ยนอินพุตเป็นการแมปเป็น "ละเว้น" จากนั้นเรียกใช้ใหม่และดูว่าข้อความหายไปหรือไม่ ในกรณีของฉันและในสภาพแวดล้อมของฉันฉันแก้ไขโดยใช้ substr ('ในการป้อนข้อมูล Teradata กับขนาดอักขระของการประกาศ ms sql สำหรับคอลัมน์เอาต์พุตตรวจสอบและตรวจสอบให้แน่ใจว่าอินพุตย่อยของคุณแพร่กระจายผ่านการแปลงข้อมูลและการแมปข้อมูลทั้งหมดของคุณใน ในกรณีที่มันไม่ได้และฉันต้องลบการแปลงข้อมูลและการแม็ปของฉันทั้งหมดและเริ่มต้นใหม่อีกครั้งตลกอีกครั้งที่ OLEDB เพิ่งจัดการมันและ ADO.net ก็โยนข้อผิดพลาดและต้องมีการแทรกแซงทั้งหมดนี้เพื่อให้มันทำงานได้โดยทั่วไปคุณ ควรใช้ OLEDB เมื่อเป้าหมายของคุณคือ MS Sql ชี้ไปที่คอลัมน์ปัญหาเมื่อมันไม่ได้ ฉันพบปัญหา colid โดยการเดาอย่างมีความรู้จากนั้นเปลี่ยนอินพุตเป็นการแมปเป็น "ละเว้น" จากนั้นเรียกใช้ใหม่และดูว่าข้อความหายไปหรือไม่ ในกรณีของฉันและในสภาพแวดล้อมของฉันฉันแก้ไขโดยใช้ substr ('ในการป้อนข้อมูล Teradata กับขนาดอักขระของการประกาศ ms sql สำหรับคอลัมน์เอาต์พุตตรวจสอบและตรวจสอบให้แน่ใจว่าอินพุตย่อยของคุณแพร่กระจายผ่านการแปลงข้อมูลและการแมปข้อมูลทั้งหมดของคุณใน ในกรณีที่มันไม่ได้และฉันต้องลบการแปลงข้อมูลและการแม็ปของฉันทั้งหมดและเริ่มต้นใหม่อีกครั้งตลกอีกครั้งที่ OLEDB เพิ่งจัดการมันและ ADO.net ก็โยนข้อผิดพลาดและต้องมีการแทรกแซงทั้งหมดนี้เพื่อให้มันทำงานได้โดยทั่วไปคุณ ควรใช้ OLEDB เมื่อเป้าหมายของคุณคือ MS Sql ชี้ไปที่คอลัมน์ปัญหาเมื่อมันไม่ได้ ฉันพบปัญหา colid โดยการเดาอย่างมีความรู้จากนั้นเปลี่ยนอินพุตเป็นการแมปเป็น "ละเว้น" จากนั้นเรียกใช้ใหม่และดูว่าข้อความหายไปหรือไม่ ในกรณีของฉันและในสภาพแวดล้อมของฉันฉันแก้ไขโดยใช้ substr ('ในการป้อนข้อมูล Teradata กับขนาดอักขระของการประกาศ ms sql สำหรับคอลัมน์เอาต์พุตตรวจสอบและตรวจสอบให้แน่ใจว่าอินพุตย่อยของคุณแพร่กระจายผ่านการแปลงข้อมูลและการแมปข้อมูลทั้งหมดของคุณใน ในกรณีที่มันไม่ได้และฉันต้องลบการแปลงข้อมูลและการแม็ปของฉันทั้งหมดและเริ่มต้นใหม่อีกครั้งตลกอีกครั้งที่ OLEDB เพิ่งจัดการมันและ ADO.net ก็โยนข้อผิดพลาดและต้องมีการแทรกแซงทั้งหมดนี้เพื่อให้มันทำงานได้โดยทั่วไปคุณ ควรใช้ OLEDB เมื่อเป้าหมายของคุณคือ MS Sql ฉันพบปัญหา colid โดยทำการเดาอย่างมีความรู้จากนั้นเปลี่ยนอินพุตเป็นการแมปเป็น "ละเว้น" จากนั้นรันใหม่และดูว่าข้อความหายไปหรือไม่ ในกรณีของฉันและในสภาพแวดล้อมของฉันฉันแก้ไขโดยใช้ substr ('ในการป้อนข้อมูล Teradata กับขนาดอักขระของการประกาศ ms sql สำหรับคอลัมน์เอาต์พุตตรวจสอบและตรวจสอบให้แน่ใจว่าอินพุตย่อยของคุณแพร่กระจายผ่านการแปลงข้อมูลและการแมปข้อมูลทั้งหมดของคุณใน ในกรณีที่มันไม่ได้และฉันต้องลบการแปลงข้อมูลและการแม็ปของฉันทั้งหมดและเริ่มต้นใหม่อีกครั้งตลกอีกครั้งที่ OLEDB เพิ่งจัดการมันและ ADO.net ก็โยนข้อผิดพลาดและต้องมีการแทรกแซงทั้งหมดนี้เพื่อให้มันทำงานได้โดยทั่วไปคุณ ควรใช้ OLEDB เมื่อเป้าหมายของคุณคือ MS Sql ฉันพบปัญหา colid โดยทำการเดาอย่างมีความรู้จากนั้นเปลี่ยนอินพุตเป็นการแมปเป็น "ละเว้น" จากนั้นรันใหม่และดูว่าข้อความหายไปหรือไม่ ในกรณีของฉันและในสภาพแวดล้อมของฉันฉันแก้ไขโดยใช้ substr ('ในการป้อนข้อมูล Teradata กับขนาดอักขระของการประกาศ ms sql สำหรับคอลัมน์เอาต์พุตตรวจสอบและตรวจสอบให้แน่ใจว่าอินพุตย่อยของคุณแพร่กระจายผ่านการแปลงข้อมูลและการแมปข้อมูลทั้งหมดของคุณใน ในกรณีที่มันไม่ได้และฉันต้องลบการแปลงข้อมูลและการแม็ปของฉันทั้งหมดและเริ่มต้นใหม่อีกครั้งตลกอีกครั้งที่ OLEDB เพิ่งจัดการมันและ ADO.net ก็โยนข้อผิดพลาดและต้องมีการแทรกแซงทั้งหมดนี้เพื่อให้มันทำงานได้โดยทั่วไปคุณ ควรใช้ OLEDB เมื่อเป้าหมายของคุณคือ MS Sql s และการแมปและเริ่มต้นใหม่อีกครั้ง ตลกอีกครั้งที่ OLEDB เพิ่งจัดการมันและ ADO.net ก็โยนข้อผิดพลาดและต้องมีการแทรกแซงทั้งหมดนี้เพื่อให้มันใช้งานได้ โดยทั่วไปคุณควรใช้ OLEDB เมื่อเป้าหมายของคุณคือ MS Sql s และการแมปและเริ่มต้นใหม่อีกครั้ง ตลกอีกครั้งที่ OLEDB เพิ่งจัดการและ ADO.net ก็โยนข้อผิดพลาดและต้องมีการแทรกแซงทั้งหมดนี้เพื่อให้มันใช้งานได้ โดยทั่วไปคุณควรใช้ OLEDB เมื่อเป้าหมายของคุณคือ MS Sql


0

ฉันเพิ่งสะดุดกับสิ่งนี้และใช้ตัวอย่างข้อมูลของ @ b_stil ฉันสามารถหาคอลัมน์ผู้ร้ายได้ และในการตรวจสอบเพิ่มเติมฉันคิดว่าฉันจำเป็นต้องตัดแต่งคอลัมน์เหมือนที่ @Liji Chandran แนะนำ แต่ฉันใช้ IExcelDataReader และฉันไม่สามารถหาวิธีง่ายๆในการตรวจสอบและตัดแต่ละคอลัมน์ 160 คอลัมน์ของฉันได้

จากนั้นฉันก็เจอคลาสนี้ (ValidatingDataReader)จากCSVReader CSVReader

สิ่งที่น่าสนใจเกี่ยวกับคลาสนี้คือให้ความยาวข้อมูลคอลัมน์ต้นทางและปลายทางแถวผู้ร้ายและแม้แต่ค่าคอลัมน์ที่ทำให้เกิดข้อผิดพลาด

สิ่งที่ฉันทำมีเพียงแค่ตัดคอลัมน์ (nvarchar, varchar, char และ nchar) ทั้งหมด

ฉันเพิ่งเปลี่ยนGetValueวิธีการเป็นสิ่งนี้:

 object IDataRecord.GetValue(int i)
    {
        object columnValue = reader.GetValue(i);

        if (i > -1 && i < lookup.Length)
        {
            DataRow columnDef = lookup[i];
            if
            (
                (
                    (string)columnDef["DataTypeName"] == "varchar" ||
                    (string)columnDef["DataTypeName"] == "nvarchar" ||
                    (string)columnDef["DataTypeName"] == "char" ||
                    (string)columnDef["DataTypeName"] == "nchar"
                ) &&
                (
                    columnValue != null &&
                    columnValue != DBNull.Value
                )
            )
            {
                string stringValue = columnValue.ToString().Trim();

                columnValue = stringValue;


                if (stringValue.Length > (int)columnDef["ColumnSize"])
                {
                    string message =
                        "Column value \"" + stringValue.Replace("\"", "\\\"") + "\"" +
                        " with length " + stringValue.Length.ToString("###,##0") +
                        " from source column " + (this as IDataRecord).GetName(i) +
                        " in record " + currentRecord.ToString("###,##0") +
                        " does not fit in destination column " + columnDef["ColumnName"] +
                        " with length " + ((int)columnDef["ColumnSize"]).ToString("###,##0") +
                        " in table " + tableName +
                        " in database " + databaseName +
                        " on server " + serverName + ".";

                    if (ColumnException == null)
                    {
                        throw new Exception(message);
                    }
                    else
                    {
                        ColumnExceptionEventArgs args = new ColumnExceptionEventArgs();

                        args.DataTypeName = (string)columnDef["DataTypeName"];
                        args.DataType = Type.GetType((string)columnDef["DataType"]);
                        args.Value = columnValue;
                        args.SourceIndex = i;
                        args.SourceColumn = reader.GetName(i);
                        args.DestIndex = (int)columnDef["ColumnOrdinal"];
                        args.DestColumn = (string)columnDef["ColumnName"];
                        args.ColumnSize = (int)columnDef["ColumnSize"];
                        args.RecordIndex = currentRecord;
                        args.TableName = tableName;
                        args.DatabaseName = databaseName;
                        args.ServerName = serverName;
                        args.Message = message;

                        ColumnException(args);

                        columnValue = args.Value;
                    }
                }



            }
        }

        return columnValue;
    }

หวังว่านี่จะช่วยใครบางคนได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.