Ricevuta una lunghezza di colonna non valida dal client bcp per colid 6


87

Voglio caricare in blocco i dati del file CSV su SQL Server 2005 dal codice c # ma riscontro l'errore seguente:

Ricevuta una lunghezza di colonna non valida dal client bcp per colid 6.

quando la copia di massa scrive sul server di database

Risposte:


69

Una delle colonne di dati in Excel (ID colonna 6) ha uno o più dati di cella che superano la lunghezza del tipo di dati della colonna di dati nel database.

Verifica i dati in Excel. Verificare anche i dati in Excel affinché il loro formato sia conforme allo schema della tabella del database.

Per evitare ciò, provare a superare la lunghezza dei dati del tipo di dati della stringa nella tabella del database.

Spero che sia di aiuto.


1
In particolare, se hai colonne VARCHAR inferiori a 4, fai attenzione a NULL nei tuoi dati che viene interpretato erroneamente come la stringa di 4 caratteri "NULL"
CrazyPyro

196

So che questo post è vecchio ma mi sono imbattuto in questo stesso problema e finalmente ho trovato una soluzione per determinare quale colonna stava causando il problema e segnalarlo se necessario. Ho determinato che colidrestituito in SqlException non è a base zero, quindi è necessario sottrarre 1 da esso per ottenere il valore. Dopodiché viene utilizzato come indice _sortedColumnMappingsdell'ArrayList dell'istanza SqlBulkCopy e non come indice dei mapping di colonna aggiunti all'istanza SqlBulkCopy. Una cosa da notare è che SqlBulkCopy si fermerà al primo errore ricevuto, quindi questo potrebbe non essere l'unico problema, ma almeno aiuta a capirlo.

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
Funziona molto bene, grazie per averci inviato.
Steven

1
Sai se è possibile ottenere anche la riga nr?
Gerhard Powell

2
DataFormatException è un'eccezione personalizzata, quindi ho potuto segnalare il problema come lunghezza della colonna non valida. Non sono ancora riuscito a capire come ottenere il numero di riga.
b_stil

1
Ottima soluzione, l'unica cosa che manca è nel catch sqlTran.RollBack ();
pqsk

8
Può essere palesemente ovvio per la maggior parte, ma "il colid restituito in SqlException non è a base zero" mi ha aiutato a spegnerlo.
Panhandel

4

Ho riscontrato un tipo di problema simile durante il passaggio di una stringa alla tabella del database utilizzando l'opzione SQL BulkCopy. La stringa che stavo passando era di 3 caratteri mentre la lunghezza della colonna di destinazione era varchar(20). Ho provato a tagliare la stringa prima di inserirla nel DB utilizzando la Trim()funzione per verificare se il problema era dovuto a uno spazio (iniziale e finale) nella stringa. Dopo aver tagliato la corda, ha funzionato bene.

Puoi provare text.Trim()


Fantastico, grazie mille! Questo è stato il motivo esatto anche nel mio caso: gli spazi vuoti finali e di conseguenza la lunghezza della colonna è stata superata.
aleor

Se la stringa potrebbe essere nulla, usa il testo? .Trim ()
Charles Plager

1

Controlla la dimensione delle colonne nella tabella che stai eseguendo inserimenti / copie in blocco. il varchar o altre colonne di stringhe potrebbero dover essere estese o il valore che stai inserendo deve essere trim. Anche l'ordine delle colonne dovrebbe essere lo stesso della tabella.

ad esempio, aumentare la dimensione della colonna varchar da 30 a 50 =>

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


0

Ottimo pezzo di codice, grazie per la condivisione!

Ho finito per usare la reflection per ottenere il DataMemberName effettivo da restituire a un client in caso di errore (sto usando il salvataggio in blocco in un servizio WCF). Spero che qualcun altro troverà utile come l'ho fatto.

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


Come può essere implementato?
Apollo

0

Ho ricevuto questo messaggio di errore con una versione ssis molto più recente (rispetto all'impresa 2015, penso che sia ssis 2016). Commenterò qui perché questo è il primo riferimento che viene visualizzato quando si google questo messaggio di errore. Penso che accada principalmente con le colonne dei caratteri quando la dimensione del carattere di origine è maggiore della dimensione del carattere di destinazione. Ho ricevuto questo messaggio quando stavo usando un input ado.net per ms sql da un database teradata. Divertente perché la precedente scrittura oledb su ms sql gestiva perfettamente tutta la conversione dei caratteri senza sovrascritture di codifica. Il numero colid e la corrispondente colonna di input di destinazione # che a volte ottieni con il messaggio colid non hanno valore. Non è la colonna quando effettui il conto alla rovescia dall'inizio della mappatura o qualcosa del genere. Se fossi Microsoft, io ' Sarebbe imbarazzante dare un messaggio di errore che sembra puntare alla colonna del problema quando non lo è. Ho trovato il problema risolvendo un'ipotesi plausibile e quindi modificando l'input per la mappatura in "Ignora" e quindi rieseguito per vedere se il messaggio è andato via. Nel mio caso e nel mio ambiente ho risolto il problema con substr (inserendo l'input Teradata alla dimensione del carattere della dichiarazione ms sql per la colonna di output. Controlla e assicurati che il tuo substr di input si propaga attraverso tutte le conversioni e mappature dei dati. Nel mio caso non ha funzionato e ho dovuto cancellare tutte le mie conversioni e mappature dei dati e ricominciare da capo. Ancora una volta divertente che OLEDB lo abbia gestito e ADO.net ha lanciato l'errore e ha dovuto fare tutto questo intervento per farlo funzionare. In generale tu dovrebbe usare OLEDB quando la destinazione è MS Sql. s indica la colonna del problema quando non lo è. Ho trovato il problema risolvendo un'ipotesi plausibile e quindi modificando l'input per la mappatura in "Ignora" e quindi rieseguito per vedere se il messaggio è andato via. Nel mio caso e nel mio ambiente ho risolto il problema con substr (inserendo l'input Teradata alla dimensione del carattere della dichiarazione ms sql per la colonna di output. Controlla e assicurati che il tuo substr di input si propaga attraverso tutte le conversioni e mappature dei dati. Nel mio caso non ha funzionato e ho dovuto cancellare tutte le mie conversioni e mappature dei dati e ricominciare da capo. Ancora una volta divertente che OLEDB lo abbia gestito e ADO.net ha lanciato l'errore e ha dovuto fare tutto questo intervento per farlo funzionare. In generale tu dovrebbe usare OLEDB quando la destinazione è MS Sql. s indica la colonna del problema quando non lo è. Ho trovato il problema risolvendo un'ipotesi plausibile e quindi modificando l'input per la mappatura in "Ignora" e quindi rieseguito per vedere se il messaggio è andato via. Nel mio caso e nel mio ambiente ho risolto il problema con substr (inserendo l'input Teradata alla dimensione del carattere della dichiarazione ms sql per la colonna di output. Controlla e assicurati che il tuo substr di input si propaga attraverso tutte le conversioni e mappature dei dati. Nel mio caso non ha funzionato e ho dovuto cancellare tutte le mie conversioni e mappature dei dati e ricominciare da capo. Ancora una volta divertente che OLEDB lo abbia gestito e ADO.net ha lanciato l'errore e ha dovuto fare tutto questo intervento per farlo funzionare. In generale tu dovrebbe usare OLEDB quando la destinazione è MS Sql. Ho trovato il problema risolvendo un'ipotesi plausibile e quindi modificando l'input per la mappatura in "Ignora" e quindi rieseguito per vedere se il messaggio è andato via. Nel mio caso e nel mio ambiente ho risolto il problema con substr (inserendo l'input Teradata alla dimensione del carattere della dichiarazione ms sql per la colonna di output. Controlla e assicurati che il tuo substr di input si propaga attraverso tutte le conversioni e mappature dei dati. Nel mio caso non ha funzionato e ho dovuto cancellare tutte le mie conversioni e mappature dei dati e ricominciare da capo. Ancora una volta divertente che OLEDB lo abbia gestito e ADO.net ha lanciato l'errore e ha dovuto fare tutto questo intervento per farlo funzionare. In generale tu dovrebbe usare OLEDB quando la destinazione è MS Sql. Ho trovato il problema risolvendo un'ipotesi plausibile e quindi modificando l'input per la mappatura in "Ignora" e quindi rieseguito per vedere se il messaggio è andato via. Nel mio caso e nel mio ambiente ho risolto il problema con substr (inserendo l'input Teradata alla dimensione del carattere della dichiarazione ms sql per la colonna di output. Controlla e assicurati che il tuo substr di input si propaga attraverso tutte le conversioni e mappature dei dati. Nel mio caso non ha funzionato e ho dovuto cancellare tutte le mie conversioni e mappature dei dati e ricominciare da capo. Ancora una volta divertente che OLEDB lo abbia gestito e ADO.net ha lanciato l'errore e ha dovuto fare tutto questo intervento per farlo funzionare. In generale tu dovrebbe usare OLEDB quando la destinazione è MS Sql. se Mapping e ricominciare da capo. Ancora una volta divertente che OLEDB lo abbia gestito e ADO.net abbia lanciato l'errore e abbia dovuto fare tutto questo intervento per farlo funzionare. In generale dovresti usare OLEDB quando il tuo obiettivo è MS Sql. se Mapping e ricominciare da capo. Ancora una volta divertente che OLEDB lo abbia gestito e ADO.net abbia lanciato l'errore e abbia dovuto fare tutto questo intervento per farlo funzionare. In generale dovresti usare OLEDB quando il tuo obiettivo è MS Sql.


0

Mi sono appena imbattuto in questo e usando lo snippet di @ b_stil, sono stato in grado di capire la colonna del colpevole. E su ulteriori indagini, ho pensato di dover tagliare la colonna proprio come suggerito da @Liji Chandran, ma stavo usando IExcelDataReader e non sono riuscito a trovare un modo semplice per convalidare e tagliare ciascuna delle mie 160 colonne.

Poi mi sono imbattuto in questa classe, (ValidatingDataReader) di CSVReader .

La cosa interessante di questa classe è che ti fornisce la lunghezza dei dati delle colonne di origine e di destinazione, la riga colpevole e persino il valore della colonna che causa l'errore.

Tutto quello che ho fatto è stato tagliare tutte le colonne (nvarchar, varchar, char e nchar).

Ho appena cambiato il mio GetValuemetodo in questo:

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

Spero che questo aiuti qualcuno

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.