Erhielt eine ungültige Spaltenlänge vom bcp-Client für Spalte 6


86

Ich möchte CSV-Dateidaten von C # -Code auf SQL Server 2005 per Bulk-Upload hochladen, aber es tritt der folgende Fehler auf:

Erhielt eine ungültige Spaltenlänge vom bcp-Client für Spalte 6.

Wenn eine Massenkopie auf den Datenbankserver geschrieben wird

Antworten:


69

Eine der Datenspalten im Excel (Spalten-ID 6) enthält eine oder mehrere Zellendaten, die die Länge des Datenspalten-Datentyps in der Datenbank überschreiten.

Überprüfen Sie die Daten in Excel. Überprüfen Sie außerdem, ob die Daten im Excel mit dem Datenbanktabellenschema übereinstimmen.

Um dies zu vermeiden, versuchen Sie, die Datenlänge des String-Datentyps in der Datenbanktabelle zu überschreiten.

Hoffe das hilft.


1
Insbesondere wenn Sie Spalten haben, die VARCHAR kleiner als 4 sind, achten Sie darauf, dass NULL in Ihren Daten als 4-Zeichen-Zeichenfolge "NULL" falsch interpretiert wird
CrazyPyro

192

Ich weiß, dass dieser Beitrag alt ist, aber ich bin auf dasselbe Problem gestoßen und habe schließlich eine Lösung gefunden, um festzustellen, welche Spalte das Problem verursacht hat, und es bei Bedarf zurückzumelden. Ich habe festgestellt, dass die colidin der SqlException zurückgegebene Version nicht auf Null basiert. Sie müssen also 1 davon subtrahieren, um den Wert zu erhalten. Danach wird es als Index der _sortedColumnMappingsArrayList der SqlBulkCopy-Instanz verwendet, nicht als Index der Spaltenzuordnungen, die der SqlBulkCopy-Instanz hinzugefügt wurden. Eine Sache zu beachten ist, dass SqlBulkCopy beim ersten empfangenen Fehler stoppt, so dass dies möglicherweise nicht das einzige Problem ist, aber zumindest hilft, es herauszufinden.

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
Dies funktioniert sehr gut, danke für die Einreichung.
Steven

1
Wissen Sie, ob es möglich ist, auch die Zeile nr zu erhalten?
Gerhard Powell

2
DataFormatException ist eine benutzerdefinierte Ausnahme, sodass ich das Problem als ungültige Spaltenlänge zurückmelden kann. Ich konnte noch nicht herausfinden, wie ich die Zeilennummer bekomme.
b_stil

1
Tolle Lösung, es fehlt nur noch der catch sqlTran.RollBack ();
pqsk

8
Für die meisten mag es offensichtlich sein, aber "die in der SqlException zurückgegebene Farbe ist nicht nullbasiert" hat mir dabei geholfen, dies zu löschen .
Panhandel

3

Beim Übergeben einer Zeichenfolge an die Datenbanktabelle mit der Option SQL BulkCopy trat ein ähnliches Problem auf. Die Zeichenfolge, die ich übergeben habe, bestand aus 3 Zeichen, während die Länge der Zielspalte betrug varchar(20). Ich habe versucht, die Zeichenfolge vor dem Einfügen in die Datenbank mithilfe der Trim()Funktion zu kürzen, um zu überprüfen, ob das Problem auf Leerzeichen (führende und nachfolgende) in der Zeichenfolge zurückzuführen ist. Nach dem Trimmen der Saite funktionierte es gut.

Du kannst es versuchen text.Trim()


Das ist großartig, vielen Dank! Das war auch in meinem Fall der genaue Grund - nachgestellte Leerzeichen und infolgedessen überschritten die Spaltenlänge.
Aleor

Wenn die Zeichenfolge null sein könnte, verwenden Sie Text? .Trim ()
Charles Plager

1

Überprüfen Sie die Größe der Spalten in der Tabelle, die Sie als Masseneinfügung / -kopie ausführen. Der varchar oder andere Zeichenfolgenspalten müssen möglicherweise erweitert werden, oder der Wert, den Sie einfügen, muss gekürzt werden. Die Spaltenreihenfolge sollte auch der Tabelle entsprechen.

zB Erhöhen Sie die Größe der Varchar-Spalte 30 auf 50 =>

ALTER TABLE [dbo]. [TableName] ALTER COLUMN [ColumnName] Varchar (50)


0

Toller Code, danke fürs Teilen!

Am Ende habe ich Reflection verwendet, um den tatsächlichen DataMemberName zu erhalten, der bei einem Fehler an einen Client zurückgegeben werden kann (ich verwende Bulk Save in einem WCF-Dienst). Hoffentlich findet jemand anderes, wie ich es gemacht habe, nützlich.

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;
}


Wie kann dies umgesetzt werden?
Apollo

0

Ich habe diese Fehlermeldung mit einer viel neueren SSIS-Version erhalten (im Vergleich zu 2015 Enterprise, ich denke, es ist SSIS 2016). Ich werde hier einen Kommentar abgeben, da dies die erste Referenz ist, die beim Google dieser Fehlermeldung angezeigt wird. Ich denke, es passiert meistens mit Zeichenspalten, wenn die Quellzeichengröße größer als die Zielzeichengröße ist. Ich habe diese Nachricht erhalten, als ich eine ado.net-Eingabe für ms sql aus einer Teradata-Datenbank verwendet habe. Witzig, weil das vorherige oledb-Schreiben in ms sql die gesamte Zeichenkonvertierung ohne Codierungsüberschreibungen perfekt handhabte. Die Colid-Nummer und die entsprechende Ziel-Eingabespalte #, die Sie manchmal mit der Colid-Nachricht erhalten, sind wertlos. Es ist nicht die Spalte, wenn Sie vom oberen Rand des Mappings herunterzählen oder ähnliches. Wenn ich Microsoft wäre, würde ich ' Es ist peinlich, eine Fehlermeldung zu geben, die so aussieht, als würde sie auf die Problemspalte zeigen, wenn dies nicht der Fall ist. Ich fand das Problem kolid, indem ich eine fundierte Vermutung anstellte und dann die Eingabe für die Zuordnung in "Ignorieren" änderte und dann erneut ausführte und prüfte, ob die Nachricht verschwunden war. In meinem Fall und in meiner Umgebung habe ich es durch substr behoben (indem ich die Teradata-Eingabe auf die Zeichengröße der ms sql-Deklaration für die Ausgabespalte eingestellt habe. Überprüfen Sie, ob sich Ihr Eingabesubstrat über alle Datenkonvertierungen und -zuordnungen verbreitet Falls dies nicht der Fall war und ich alle meine Datenkonvertierungen und Zuordnungen löschen und von vorne beginnen musste. Wieder komisch, dass OLEDB es gerade behandelt hat und ADO.net den Fehler geworfen hat und all diese Eingriffe haben musste, damit es funktioniert. Im Allgemeinen Sie sollte OLEDB verwenden, wenn Ihr Ziel MS Sql ist. s zeigt auf die Problemspalte, wenn dies nicht der Fall ist. Ich fand das Problem kolid, indem ich eine fundierte Vermutung anstellte und dann die Eingabe für die Zuordnung in "Ignorieren" änderte und dann erneut ausführte und prüfte, ob die Nachricht verschwunden war. In meinem Fall und in meiner Umgebung habe ich es durch substr behoben (indem ich die Teradata-Eingabe auf die Zeichengröße der ms sql-Deklaration für die Ausgabespalte eingestellt habe. Überprüfen Sie, ob sich Ihr Eingabesubstrat über alle Datenkonvertierungen und -zuordnungen verbreitet Falls dies nicht der Fall war und ich alle meine Datenkonvertierungen und Zuordnungen löschen und von vorne beginnen musste. Wieder komisch, dass OLEDB es gerade behandelt hat und ADO.net den Fehler geworfen hat und all diese Eingriffe haben musste, damit es funktioniert. Im Allgemeinen Sie sollte OLEDB verwenden, wenn Ihr Ziel MS Sql ist. s zeigt auf die Problemspalte, wenn dies nicht der Fall ist. Ich fand das Problem kolid, indem ich eine fundierte Vermutung anstellte und dann die Eingabe für die Zuordnung in "Ignorieren" änderte und dann erneut ausführte und prüfte, ob die Nachricht verschwunden war. In meinem Fall und in meiner Umgebung habe ich es durch substr behoben (indem ich die Teradata-Eingabe auf die Zeichengröße der ms sql-Deklaration für die Ausgabespalte eingestellt habe. Überprüfen Sie, ob sich Ihr Eingabesubstrat über alle Datenkonvertierungen und -zuordnungen verbreitet Falls dies nicht der Fall war und ich alle meine Datenkonvertierungen und Zuordnungen löschen und von vorne beginnen musste. Wieder komisch, dass OLEDB es gerade behandelt hat und ADO.net den Fehler geworfen hat und all diese Eingriffe haben musste, damit es funktioniert. Im Allgemeinen Sie sollte OLEDB verwenden, wenn Ihr Ziel MS Sql ist. Ich fand das Problem kolid, indem ich eine fundierte Vermutung anstellte und dann die Eingabe für die Zuordnung in "Ignorieren" änderte und dann erneut ausführte und prüfte, ob die Nachricht verschwunden war. In meinem Fall und in meiner Umgebung habe ich es durch substr behoben (indem ich die Teradata-Eingabe auf die Zeichengröße der ms sql-Deklaration für die Ausgabespalte eingestellt habe. Überprüfen Sie, ob sich Ihr Eingabesubstrat über alle Datenkonvertierungen und -zuordnungen verbreitet Falls dies nicht der Fall war und ich alle meine Datenkonvertierungen und Zuordnungen löschen und von vorne beginnen musste. Wieder komisch, dass OLEDB es gerade behandelt hat und ADO.net den Fehler geworfen hat und all diese Eingriffe haben musste, damit es funktioniert. Im Allgemeinen Sie sollte OLEDB verwenden, wenn Ihr Ziel MS Sql ist. Ich fand das Problem kolid, indem ich eine fundierte Vermutung anstellte und dann die Eingabe für die Zuordnung in "Ignorieren" änderte und dann erneut ausführte und prüfte, ob die Nachricht verschwunden war. In meinem Fall und in meiner Umgebung habe ich es durch substr behoben (indem ich die Teradata-Eingabe auf die Zeichengröße der ms sql-Deklaration für die Ausgabespalte eingestellt habe. Überprüfen Sie, ob sich Ihr Eingabesubstrat über alle Datenkonvertierungen und -zuordnungen verbreitet Falls dies nicht der Fall war und ich alle meine Datenkonvertierungen und Zuordnungen löschen und von vorne beginnen musste. Wieder komisch, dass OLEDB es gerade behandelt hat und ADO.net den Fehler geworfen hat und all diese Eingriffe haben musste, damit es funktioniert. Im Allgemeinen Sie sollte OLEDB verwenden, wenn Ihr Ziel MS Sql ist. s und Mappings und von vorne beginnen. Wieder lustig, dass OLEDB gerade damit umgegangen ist und ADO.net den Fehler geworfen hat und all diese Eingriffe haben musste, damit es funktioniert. Im Allgemeinen sollten Sie OLEDB verwenden, wenn Ihr Ziel MS Sql ist. s und Mappings und von vorne beginnen. Wieder lustig, dass OLEDB gerade damit umgegangen ist und ADO.net den Fehler geworfen hat und all diese Eingriffe haben musste, damit es funktioniert. Im Allgemeinen sollten Sie OLEDB verwenden, wenn Ihr Ziel MS Sql ist.


0

Ich bin gerade darauf gestoßen und konnte mit dem Snippet von @ b_stil die Spalte mit den Tätern herausfinden. Bei weiteren Untersuchungen stellte ich fest, dass ich die Spalte genau so zuschneiden musste, wie @Liji Chandran vorgeschlagen hatte, aber ich verwendete IExcelDataReader und konnte keine einfache Möglichkeit finden, jede meiner 160 Spalten zu validieren und zu beschneiden.

Dann bin ich auf diese Klasse gestoßen (ValidatingDataReader) von CSVReader .

Das Interessante an dieser Klasse ist, dass sie die Datenlänge der Quell- und Zielspalten, die Täterzeile und sogar den Spaltenwert angibt, der den Fehler verursacht.

Ich habe nur alle Spalten (nvarchar, varchar, char und nchar) abgeschnitten.

Ich habe gerade meine GetValueMethode dahingehend geändert :

 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;
    }

Hoffe das hilft jemandem

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.