Bagaimana cara mengidentifikasi kolom yang bertanggung jawab untuk "Data string atau biner akan terpotong."

31

Saya menghasilkan beberapa pertanyaan secara otomatis dengan kode yang saya tulis untuk SELECT dari basis data Pg jarak jauh, dan masukkan ke dalam basis data SQL Server lokal. Namun, salah satunya menghasilkan kesalahan ini:

[Microsoft] [ODBC SQL Server Driver] [SQL Server] Data string atau biner akan terpotong. (SQL-22001) [status adalah 22001 sekarang 01000]

[Microsoft] [ODBC SQL Server Driver] [SQL Server] Pernyataan telah diakhiri. (SQL-01000) di. \ Insert.pl baris 106.

Bagaimana cara mengetahui kolom apa yang menghasilkan kesalahan itu dan tidak memiliki panjang untuk input? Apakah ada cara untuk melakukan ini tanpa menebak-nebak dengan paksa semua varchar?

Evan Carroll
sumber

Jawaban:

35

Tidak, ini tidak dicatat di mana pun. Pergi memilih dan nyatakan kasus bisnis Anda; ini adalah salah satu dari daftar panjang hal-hal yang harus diperbaiki di SQL Server.

Ini diminta tahun lalu di Connect (mungkin pertama kali dalam jangka waktu SQL Server 2000 atau 2005), kemudian lagi pada sistem umpan balik baru:

Dan sekarang telah dikirim, di SQL Server 2019 , SQL Server 2017 CU12, dan akan muncul di SQL Server 2016 SP2 CU di masa depan.

Dalam CTP publik pertama dari SQL Server 2019, itu hanya muncul di bawah bendera jejak 460. Ini kedengarannya agak rahasia, tetapi diterbitkan dalam whitepaper Microsoft ini . Ini akan menjadi perilaku default (tidak ada tanda jejak diperlukan) ke depan, meskipun Anda akan dapat mengontrol ini melalui konfigurasi cakupan database baru VERBOSE_TRUNCATION_WARNINGS.

Berikut ini sebuah contoh:

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

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

Menghasilkan semua versi yang didukung sebelum SQL Server 2019:

Msg 8152, Level 16, State 30, Line 5
String atau data biner akan terpotong.
Pernyataan itu telah dihentikan.

Sekarang, di SQL Server 2019 CTP, dengan tanda jejak diaktifkan:

DBCC TRACEON(460);
GO

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

Hasil menunjukkan tabel, kolom, dan nilai ( terpotong , tidak penuh ):

Msg 2628, Level 16,
Status 1, Baris 11 String atau data biner akan terpotong di tabel 'tempdb.dbo.x', kolom 'a'. Nilai terpotong: 'f'.
Pernyataan itu telah dihentikan.

Sampai Anda dapat membuang semuanya dan meningkatkan ke SQL Server 2019, atau pindah ke Azure SQL Database, Anda dapat mengubah kode "automagic" Anda untuk benar-benar menarik max_length dari sys.columns, bersama dengan nama yang harus Anda tuju di sana, dan kemudian menerapkan LEFT(column, max_length)atau apa pun yang setara dengan PG. Atau, karena itu berarti Anda akan secara diam-diam kehilangan data, cari tahu kolom apa yang tidak cocok dan perbaiki kolom tujuan sehingga cocok dengan semua data dari sumber. Diberikan akses metadata ke kedua sistem, dan fakta bahwa Anda sudah menulis kueri yang harus secara otomatis mencocokkan sumber -> kolom tujuan (jika tidak, kesalahan ini tidak akan menjadi masalah terbesar Anda), Anda tidak harus melakukan brute-force menebak sama sekali.

Aaron Bertrand
sumber
2

Jika Anda memiliki akses untuk menjalankan SQL Server Import and Export Wizard dari SQL Server Management Studio (klik kanan database> Tasks> Import Data ...), buat tugas yang mengimpor dari SQL Client menggunakan kueri Anda sebagai sumber data ke tujuan meja.

Sebelum Anda menjalankan impor, Anda dapat meninjau pemetaan data dan itu akan memberi tahu Anda kolom mana yang memiliki tipe bidang yang tidak konsisten. Dan jika Anda menjalankan tugas impor, ia akan memberi tahu Anda kolom mana yang gagal diimpor.

Contoh Peringatan Validasi:

Peringatan 0x802092a7: Aliran Data Tugas 1: Pemotongan mungkin terjadi karena memasukkan data dari kolom aliran data "NARRATIVE" dengan panjang 316 ke kolom database "NARRATIVE" dengan panjang 60. (SQL Server Impor dan Ekspor Wizard)

bubbassauro
sumber
1

Pada akhirnya, saya tidak dapat menemukan cara untuk mendapatkan informasi kolom tanpa menulis sendiri.

Pesan kesalahan ini dihasilkan oleh DBD::ODBC, namun Anda juga dapat menggunakan sys.columns (max_length)(Saya tidak tahu caranya).

Saya menggunakan kode seperti ini di daftar kolom saya untuk mendapatkan daftar array dengan dua elemen, yaitu COLUMN_NAME, dan MAX_LENGTH(didokumentasikan dalam DBIcolumn_info() ).

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

Kemudian saya menangkap pengecualian INSERTdan mencetak sesuatu yang bermanfaat. Dalam contoh ini @$rowadalah data yang dikirim kesth->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;
}

Juga, silakan pilih dan pilih jawaban yang lain

Evan Carroll
sumber
2
Saya tidak menaruh referensi kode sys.columnskarena saya sama sekali tidak tahu kode apa yang saat ini Anda gunakan untuk "secara otomatis" menghasilkan pertanyaan Anda. Sebenarnya tidak terlalu rumit yang bisa saya tebak untuk dimasukkan ke dalam kode Anda SELECT name, object_id, max_length FROM sys.columns;. Karena Anda sudah memiliki kode automagic yang harus melakukan ini - atau sesuatu yang sangat mirip - saya tidak berpikir contoh diperlukan.
Aaron Bertrand
Saya tidak yakin cara sys.columnskerjanya dengan dua kolom yang sama name. Juga, saya mendapatkan hal yang berfungsi menggunakan perpustakaan daripada sys, mengapa saya menjadikan itu sebagai jawaban yang dipilih? Microsoft SQL doesn't have x, do y insteadadalah kontribusi yang valid, tetapi jika Anda ylebih rendah dari saya y, saya akan melakukan sesuatu yang berbeda dan menandainya sebagai yang dipilih.
Evan Carroll
1
Pertanyaan Anda adalah, pada dasarnya, bagaimana saya mengetahui kolom apa yang menghasilkan kesalahan (mungkin, sehingga Anda dapat memperbaiki satu tempat itu, alih-alih merekayasa ulang solusi). Saya katakan di mana harus mencari: sys.columns. Di mana tepatnya Anda harus melihat untuk membandingkan panjang kolom sumber Anda dengan panjang kolom tujuan. Bagaimana Anda melakukannya, itu terserah Anda. Saya tidak memberi tahu Anda cara memperbaiki kode Anda, karena saya sama sekali tidak tahu bagaimana permintaan automagic Anda dihasilkan, jadi, seperti yang saya katakan, tidak tahu cara menambahkan penentuan panjang untuk kueri apa pun yang sudah Anda miliki .
Aaron Bertrand
1

Akhirnya Microsoft telah memutuskan untuk memberikan informasi yang bermakna untuk String or binary would be truncatedmulai dari SQL Server 2016 SP2 CU, SQL Server 2017 CU12 dan dalam SQL Server 2019.

Informasi itu sekarang termasuk kolom tabel yang menyinggung (nama yang memenuhi syarat penuh) dan nilai yang menyinggung (terpotong pada 120 karakter):

Msg 2628, Level 16, Negara 1, Baris x String atau data biner akan terpotong di tabel 'TheDb. Skema. Tabel', kolom 'Kolom'. Nilai terpotong: '...'. Pernyataan itu telah dihentikan.

Alexei
sumber