Jak zidentyfikować kolumny odpowiedzialne za „Łańcuch lub dane binarne zostałyby obcięte”.

31

Generuję zapytania automatycznie za pomocą kodu, który napisałem do SELECT ze zdalnej bazy danych Pg i wstawiam do lokalnej bazy danych SQL Server. Jednak jeden z nich generuje ten błąd:

[Microsoft] [ODBC SQL Server Driver] [SQL Server] Łańcuch lub dane binarne zostałyby obcięte. (SQL-22001) [stan to 22001 teraz 01000]

[Microsoft] [ODBC SQL Server Driver] [SQL Server] Instrukcja została zakończona. (SQL-01000) w. \ Insert.pl wiersz 106.

Jak dowiedzieć się, która kolumna generuje ten błąd i brakuje długości danych wejściowych? Czy można to zrobić bez zgadywania z całą siłą varchar?

Evan Carroll
źródło

Odpowiedzi:

35

Nie, nigdzie nie jest zalogowany. Głosuj i podaj swoje uzasadnienie biznesowe; jest to jedna z długiej listy rzeczy, które powinny zostać naprawione w SQL Server.

To było wymagane wiele lat temu w Connect (prawdopodobnie najpierw w ramach czasowych SQL Server 2000 lub 2005), a następnie w nowym systemie informacji zwrotnej:

A teraz został dostarczony w SQL Server 2019 , SQL Server 2017 CU12, i pojawi się w przyszłym SQL Server 2016 SP2 CU.

W pierwszym publicznym CTP programu SQL Server 2019 pojawia się tylko pod flagą śledzenia 460. Brzmi to trochę sekretnie, ale zostało opublikowane w białej księdze Microsoft . Będzie to zachowanie domyślne (nie wymaga flagi śledzenia), ale będziesz mógł to kontrolować za pomocą nowej konfiguracji o zasięgu baz danych VERBOSE_TRUNCATION_WARNINGS.

Oto przykład:

USE tempdb;
GO
CREATE TABLE dbo.x(a char(1));

INSERT dbo.x(a) VALUES('foo');
GO

Wynik we wszystkich obsługiwanych wersjach wcześniejszych niż SQL Server 2019:

Msg 8152, poziom 16, stan 30, wiersz 5
Ciąg lub dane binarne zostałyby obcięte.
Instrukcja została zakończona.

Teraz na serwerach CTP programu SQL Server 2019 z włączoną flagą śledzenia:

DBCC TRACEON(460);
GO

INSERT dbo.x(a) VALUES('foo');
GO
DROP TABLE dbo.x;
DBCC TRACEOFF(460);

Wynik pokazuje tabelę, kolumnę i wartość ( obciętą , niepełną ):

Msg 2628, poziom 16, stan 1, wiersz 11 Dane
łańcuchowe lub binarne zostałyby obcięte w tabeli „tempdb.dbo.x”, kolumna „a”. Skrócona wartość: „f”.
Instrukcja została zakończona.

Dopóki nie możesz porzucić wszystkiego i uaktualnić do SQL Server 2019 lub przejść do bazy danych SQL Azure, możesz zmienić kod „automagiczny”, aby faktycznie pobierał maksymalną długość sys.columns, wraz z nazwą, którą i tak musisz tam dostać, a następnie zastosować LEFT(column, max_length)lub niezależnie od ekwiwalentu PG. Lub, ponieważ oznacza to po prostu, że po cichu stracisz dane, dowiedz się, które kolumny są niedopasowane i napraw kolumny docelowe, aby pasowały do ​​wszystkich danych ze źródła. Biorąc pod uwagę dostęp do metadanych do obu systemów oraz fakt, że już piszesz zapytanie, które musi automatycznie dopasowywać źródło -> kolumny docelowe (w przeciwnym razie ten błąd nie byłby twoim największym problemem), nie powinieneś robić żadnej brutalnej siły zgadywanie w ogóle.

Aaron Bertrand
źródło
2

Jeśli masz dostęp do Kreatora importu i eksportu programu SQL Server z SQL Server Management Studio (baza danych prawym przyciskiem myszy> Zadania> Importuj dane ...), utwórz zadanie, które importuje z klienta SQL za pomocą zapytania jako źródła danych do miejsca docelowego stół.

Przed uruchomieniem importu możesz przejrzeć mapowanie danych, aby dowiedzieć się, które kolumny mają niespójne typy pól. A jeśli uruchomisz zadanie importu, powie Ci, które kolumny nie zostały zaimportowane.

Ostrzeżenie o walidacji próbki:

Ostrzeżenie 0x802092a7: Zadanie przepływu danych 1: Obcięcie może wystąpić z powodu wstawienia danych z kolumny przepływu danych „NARRATIVE” o długości 316 do kolumny bazy danych „NARRATIVE” o długości 60. (SQL Server Import and Export Wizard)

bubbassauro
źródło
1

Ostatecznie nie mogłem znaleźć sposobu na uzyskanie informacji o kolumnie bez samodzielnego napisania.

Ten komunikat o błędzie został wygenerowany przez DBD::ODBC, możesz jednak również użyć sys.columns (max_length)(po prostu nie wiem jak).

Użyłem takiego kodu na mojej liście kolumn, aby uzyskać listę tablic z dwoma elementami, the COLUMN_NAMEi MAX_LENGTH(udokumentowane w DBIcolumn_info() ).

my @max_lengths = map [ @{$_->fetchall_arrayref->[0]}[3,6] ]
    , map $dbh_mssql->column_info('database', 'dbo', $dest_table, $_)
    , @col_mssql
;

Potem wychwyciłem wyjątki INSERTi wydrukowałem coś pożytecznego. W tym przykładzie @$rowdane są wysyłane dosth->execute()

if ($@) {
        warn "$@\n";
        for ( my $idx=0; $idx <= $#{ $row }; $idx++ ) {
                Dumper {
                        maxlength => $max_lengths[$idx]->[1]
                        , name    => $max_lengths[$idx]->[0]
                        , length  => length( $row->[$idx] )
                        , content => $row->[$idx]
                };
        }
        die;
}

Proszę również głosować i głosować za drugą odpowiedzią

Evan Carroll
źródło
2
Nie umieściłem żadnego odwołania do kodu, sys.columnsponieważ absolutnie nie miałem pojęcia, jakiego kodu używasz obecnie do „automatycznego” generowania zapytań. Naprawdę nie ma o wiele bardziej złożonego, o czym mógłbym się domyślić w kwestii włączenia do twojego kodu niż SELECT name, object_id, max_length FROM sys.columns;. Ponieważ masz już kod automagiczny, który musi to robić - lub coś bardzo podobnego - nie sądziłem, że przykład jest potrzebny.
Aaron Bertrand
Nie jestem pewien, jak sys.columnsdziała z dwiema kolumnami, które mają takie same name. Mam też problem z biblioteką sys, dlaczego miałbym to zrobić jako wybraną odpowiedź? Microsoft SQL doesn't have x, do y insteadjest ważnym wkładem, ale jeśli twój yjest gorszy od mojego y, zrobię coś innego i oznaczę to jako wybrane.
Evan Carroll,
1
Twoje pytanie było w zasadzie, jak dowiedzieć się, która kolumna generuje błąd (przypuszczalnie, abyś mógł naprawić to jedno miejsce, zamiast przeprojektowywać rozwiązanie). Mówiłem ci, gdzie szukać: sys.columns. Właśnie w tym miejscu powinieneś porównać długości kolumn źródłowych z długościami kolumn docelowych. To, jak to zrobisz, zależy od ciebie. Nie powiedziałem ci, jak naprawić kod, ponieważ nie mam pojęcia, jak generowane jest Twoje zapytanie automagiczne, więc, jak powiedziałem, nie miałem pojęcia, jak dodać oznaczenia długości do dowolnego zapytania, które już miałeś .
Aaron Bertrand
1

Wreszcie Microsoft zdecydował się podać istotne informacje na String or binary would be truncatedpoczątek od SQL Server 2016 SP2 CU, SQL Server 2017 CU12 i SQL Server 2019.

Informacje obejmują teraz zarówno obrażającą kolumnę tabeli (pełna nazwa), jak i obrażającą wartość (skróconą do 120 znaków):

Msg 2628, poziom 16, stan 1, wiersz x ciąg lub dane binarne zostałyby obcięte w tabeli „TheDb.TheSchema.TheTable”, kolumna „TheColumn”. Skrócona wartość: „...”. Instrukcja została zakończona.

Aleksiej
źródło