Chcę zbiorczo przesłać dane pliku csv na serwer sql 2005 z kodu C #, ale napotykam poniższy błąd -
Odebrano nieprawidłową długość kolumny od klienta bcp dla colid 6.
podczas kopiowania zbiorczego zapisuj na serwerze bazy danych
Odpowiedzi:
Jedna z kolumn danych w programie Excel (identyfikator kolumny 6) zawiera co najmniej jedną komórkę danych, które przekraczają długość typu danych kolumny danych w bazie danych.
Zweryfikuj dane w programie Excel. Sprawdź również dane w programie Excel pod kątem zgodności formatu ze schematem tabeli bazy danych.
Aby tego uniknąć, spróbuj przekroczyć długość danych typu łańcuchowego w tabeli bazy danych.
Mam nadzieję że to pomoże.
Wiem, że ten post jest stary, ale napotkałem ten sam problem i ostatecznie znalazłem rozwiązanie, aby określić, która kolumna powoduje problem i zgłosić go w razie potrzeby. colid
Ustaliłem, że zwracany w SqlException nie jest oparty na zerze, więc musisz odjąć od niego 1, aby uzyskać wartość. Następnie jest używany jako indeks _sortedColumnMappings
ArrayList wystąpienia SqlBulkCopy, a nie indeks mapowań kolumn, które zostały dodane do wystąpienia SqlBulkCopy. Należy zauważyć, że SqlBulkCopy zatrzyma się przy pierwszym otrzymanym błędzie, więc może to nie być jedyny problem, ale przynajmniej pomaga go rozgryźć.
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;
}
Napotkałem podobny problem podczas przekazywania ciągu do tabeli bazy danych za pomocą opcji SQL BulkCopy. Przekazywany przeze mnie ciąg miał 3 znaki, podczas gdy długość kolumny docelowej wynosiła varchar(20)
. Próbowałem przyciąć ciąg przed wstawieniem go do bazy danych za pomocą Trim()
funkcji, aby sprawdzić, czy problem jest spowodowany spacją (początkową i końcową) w ciągu. Po przycięciu sznurka działało dobrze.
Możesz spróbować text.Trim()
Sprawdź rozmiar kolumn w tabeli, którą robisz zbiorczo wstawiaj / kopiuj. varchar lub inne kolumny łańcuchowe mogą wymagać rozszerzenia lub wartość, którą wstawiasz, musi zostać przycięta. Kolejność kolumn również powinna być taka sama jak w tabeli.
np. Zwiększ rozmiar kolumny varchar od 30 do 50 =>
ALTER TABLE [dbo]. [TableName] ALTER COLUMN [ColumnName] Varchar (50)
Świetny fragment kodu, dzięki za udostępnienie!
Skończyło się na korzystaniu z odbicia, aby uzyskać faktyczny DataMemberName, aby wyrzucić z powrotem do klienta po błędzie (używam zapisywania zbiorczego w usłudze WCF). Mam nadzieję, że ktoś inny uzna, jak to zrobiłem.
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;
}
Otrzymałem ten komunikat o błędzie z dużo nowszą wersją ssis (w porównaniu z 2015 Enterprise, myślę, że to ssis 2016). Skomentuję tutaj, ponieważ jest to pierwsza wzmianka, która pojawia się podczas wygooglowania tego komunikatu o błędzie. Myślę, że dzieje się tak głównie w przypadku kolumn znaków, gdy rozmiar znaku źródłowego jest większy niż rozmiar znaku docelowego. Otrzymałem tę wiadomość, gdy korzystałem z danych wejściowych ado.net do ms sql z bazy danych teradata. Zabawne, ponieważ poprzedni oledb zapisuje w ms sql doskonale radził sobie z konwersją znaków bez przesłonięć kodowania. Liczba zbiorcza i odpowiadająca jej kolumna wprowadzania adresu docelowego, którą czasami otrzymujesz z treścią wiadomości, są bezwartościowe. To nie jest kolumna, gdy odliczasz od góry mapowania lub coś w tym rodzaju. Gdybym był microsoftem, daj się zawstydzić, gdy pojawi się komunikat o błędzie, który wygląda tak, jakby wskazywał kolumnę z problemem, podczas gdy tak nie jest. Znalazłem problem, wykonując przemyślane przypuszczenia, a następnie zmieniając dane wejściowe mapowania na „Ignoruj”, a następnie uruchomiłem ponownie i sprawdzić, czy komunikat zniknął. W moim przypadku i w moim środowisku naprawiłem to przez substr (wprowadzając dane wejściowe Teradata do rozmiaru znaku deklaracji ms sql dla kolumny wyjściowej. Sprawdź i upewnij się, że substrat wejściowy propaguje się przez wszystkie konwersje i mapowania danych. jeśli tak się nie stało i musiałem usunąć wszystkie moje konwersje danych i mapowania i zacząć od nowa. Znowu zabawne, że OLEDB właśnie sobie z tym poradził, a ADO.net wyrzucił błąd i musiał wykonać całą tę interwencję, aby działał. Ogólnie rzecz biorąc, powinien używać OLEDB, gdy celem jest MS Sql. s wskazuje kolumnę z problemem, gdy tak nie jest. Znalazłem problem, wykonując przemyślane przypuszczenia, a następnie zmieniając dane wejściowe mapowania na „Ignoruj”, a następnie uruchomiłem ponownie i sprawdzić, czy komunikat zniknął. W moim przypadku i w moim środowisku naprawiłem to przez substr (wprowadzając dane wejściowe Teradata do rozmiaru znaku deklaracji ms sql dla kolumny wyjściowej. Sprawdź i upewnij się, że substrat wejściowy propaguje się przez wszystkie konwersje i mapowania danych. jeśli tak się nie stało i musiałem usunąć wszystkie moje konwersje danych i mapowania i zacząć od nowa. Znowu zabawne, że OLEDB właśnie sobie z tym poradził, a ADO.net wyrzucił błąd i musiał wykonać całą tę interwencję, aby działał. Ogólnie rzecz biorąc, powinien używać OLEDB, gdy celem jest MS Sql. s wskazuje kolumnę z problemem, gdy tak nie jest. Znalazłem problem, wykonując przemyślane przypuszczenia, a następnie zmieniając dane wejściowe mapowania na „Ignoruj”, a następnie uruchomiłem ponownie i sprawdzić, czy komunikat zniknął. W moim przypadku i w moim środowisku naprawiłem to przez substr (wprowadzając dane wejściowe Teradata do rozmiaru znaku deklaracji ms sql dla kolumny wyjściowej. Sprawdź i upewnij się, że substrat wejściowy propaguje się przez wszystkie konwersje i mapowania danych. jeśli tak się nie stało i musiałem usunąć wszystkie moje konwersje danych i mapowania i zacząć od nowa. Znowu zabawne, że OLEDB właśnie sobie z tym poradził, a ADO.net wyrzucił błąd i musiał wykonać całą tę interwencję, aby działał. Ogólnie rzecz biorąc, powinien używać OLEDB, gdy celem jest MS Sql. Znalazłem problem, wykonując przemyślane przypuszczenia, a następnie zmieniając dane wejściowe mapowania na „Ignoruj”, a następnie uruchomiłem ponownie i sprawdzić, czy komunikat zniknął. W moim przypadku i w moim środowisku naprawiłem to przez substr (wprowadzając dane wejściowe Teradata do rozmiaru znaku deklaracji ms sql dla kolumny wyjściowej. Sprawdź i upewnij się, że substrat wejściowy propaguje się przez wszystkie konwersje i mapowania danych. jeśli tak się nie stało i musiałem usunąć wszystkie moje konwersje danych i mapowania i zacząć od nowa. Znowu zabawne, że OLEDB właśnie sobie z tym poradził, a ADO.net wyrzucił błąd i musiał wykonać całą tę interwencję, aby działał. Ogólnie rzecz biorąc, powinien używać OLEDB, gdy celem jest MS Sql. Znalazłem problem, wykonując przemyślane przypuszczenia, a następnie zmieniając dane wejściowe mapowania na „Ignoruj”, a następnie uruchomiłem ponownie i sprawdzić, czy komunikat zniknął. W moim przypadku i w moim środowisku naprawiłem to przez substr (wprowadzając dane wejściowe Teradata do rozmiaru znaku deklaracji ms sql dla kolumny wyjściowej. Sprawdź i upewnij się, że substrat wejściowy propaguje się przez wszystkie konwersje i mapowania danych. jeśli tak się nie stało i musiałem usunąć wszystkie moje konwersje danych i mapowania i zacząć od nowa. Znowu zabawne, że OLEDB właśnie sobie z tym poradził, a ADO.net wyrzucił błąd i musiał wykonać całą tę interwencję, aby działał. Ogólnie rzecz biorąc, powinien używać OLEDB, gdy celem jest MS Sql. si Mappings i zacznij od nowa. Znowu zabawne, że OLEDB właśnie sobie z tym poradził, a ADO.net rzucił błąd i musiał wykonać całą interwencję, aby zadziałał. Generalnie powinieneś używać OLEDB, gdy Twoim celem jest MS Sql. si Mappings i zacznij od nowa. Znowu zabawne, że OLEDB właśnie sobie z tym poradził, a ADO.net rzucił błąd i musiał wykonać całą interwencję, aby zadziałał. Generalnie powinieneś używać OLEDB, gdy Twoim celem jest MS Sql.
Właśnie się na to natknąłem i używając fragmentu @ b_stil, byłem w stanie znaleźć kolumnę winowajców. Po dalszym dochodzeniu doszedłem do wniosku, że muszę przyciąć kolumnę, tak jak sugerował @Liji Chandran, ale używałem IExcelDataReader i nie mogłem znaleźć prostego sposobu na sprawdzenie poprawności i przycięcie każdej z moich 160 kolumn.
Potem natknąłem się na tę klasę, klasę (ValidatingDataReader) z CSVReader .
Interesującą rzeczą w tej klasie jest to, że podaje ona długość danych kolumny źródłowej i docelowej, wiersz sprawcy, a nawet wartość kolumny, która powoduje błąd.
Wszystko, co zrobiłem, to przycięcie wszystkich kolumn (nvarchar, varchar, char i nchar).
Właśnie zmieniłem GetValue
metodę na następującą:
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;
}
Mam nadzieję, że to komuś pomoże