Como identifico a (s) coluna (s) responsável (s) por "String ou dados binários seriam truncados".

31

Estou gerando algumas consultas automaticamente com o código que escrevi para SELECT a partir de um banco de dados Pg remoto e insiro em um banco de dados local do SQL Server. No entanto, um deles está gerando este erro:

[Microsoft] [Driver ODBC do SQL Server] [SQL Server] Seqüência de caracteres ou dados binários seriam truncados. (SQL-22001) [o estado era 22001 agora 01000]

[Microsoft] [Driver ODBC do SQL Server] [SQL Server] A instrução foi finalizada. (SQL-01000) na linha \ insert.pl 106.

Como descubro qual coluna está gerando esse erro e não possui o comprimento da entrada? Existe uma maneira de fazer isso sem adivinhar a força bruta varchar?

Evan Carroll
fonte

Respostas:

35

Não, não está registrado em nenhum lugar. Vá votar e indique seu caso de negócios; esse é um dos itens que devem ser corrigidos no SQL Server.

Isso foi solicitado anos atrás no Connect (provavelmente primeiro no período de tempo do SQL Server 2000 ou 2005) e depois novamente no novo sistema de feedback:

E agora ele foi entregue no SQL Server 2019 , SQL Server 2017 CU12 e aparecerá em uma futura CU do SQL Server 2016 SP2.

No primeiro CTP público do SQL Server 2019, ele aparece apenas sob o sinalizador de rastreamento 460. Isso parece meio secreto, mas foi publicado neste whitepaper da Microsoft . Esse será o comportamento padrão (nenhum sinalizador de rastreamento necessário) daqui para frente, embora você possa controlar isso por meio de uma nova configuração de escopo do banco de dados VERBOSE_TRUNCATION_WARNINGS.

Aqui está um exemplo:

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

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

Resultar em todas as versões suportadas antes do SQL Server 2019:

Msg 8152, nível 16, estado 30, linha 5
String ou dados binários seriam truncados.
A instrução foi encerrada.

Agora, nos CTPs do SQL Server 2019, com o sinalizador de rastreamento ativado:

DBCC TRACEON(460);
GO

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

O resultado mostra a tabela, a coluna e o valor ( truncado , não completo ):

Msg 2628, Nível 16, Estado 1, Linha 11
Seqüência de caracteres ou dados binários seriam truncados na tabela 'tempdb.dbo.x', coluna 'a'. Valor truncado: 'f'.
A instrução foi encerrada.

Até que você possa largar tudo e atualizar para o SQL Server 2019 ou migrar para o Banco de Dados SQL do Azure, você pode alterar o código "automagic" para realmente extrair o max_length sys.columns, juntamente com o nome do qual você deve chegar lá e aplicar LEFT(column, max_length)ou qualquer que seja o equivalente do PG. Ou, como isso significa que você perderá dados silenciosamente, descubra quais colunas são incompatíveis e corrija as colunas de destino para que elas se ajustem a todos os dados da origem. Dado o acesso de metadados aos dois sistemas e o fato de você já estar escrevendo uma consulta que deve corresponder automaticamente a colunas de origem -> destino (caso contrário, esse erro dificilmente seria o seu maior problema), não seria necessário fazer força bruta adivinhando.

Aaron Bertrand
fonte
2

Se você tiver acesso para executar o Assistente para Importação e Exportação do SQL Server no SQL Server Management Studio (clique com o botão direito do mouse em banco de dados> Tarefas> Importar Dados ...), crie uma tarefa que importe do SQL Client usando sua consulta como fonte de dados para o destino tabela.

Antes de executar a importação, você pode revisar o mapeamento de dados e ele informará quais colunas têm tipos de campo inconsistentes. E se você executar a tarefa de importação, ela informará quais colunas não foram importadas.

Aviso de validação de amostra:

Aviso 0x802092a7: Tarefa 1 de fluxo de dados: O truncamento pode ocorrer devido à inserção de dados da coluna "NARRATIVE" do fluxo de dados com um comprimento de 316 na coluna "NARRATIVE" do banco de dados com um comprimento de 60. (Assistente para Importação e Exportação do SQL Server)

bubbassauro
fonte
1

Por fim, não consegui encontrar uma maneira de obter as informações da coluna sem escrevê-las.

Esta mensagem de erro foi gerada por DBD::ODBC, você também pode usar sys.columns (max_length)(eu simplesmente não sei como).

Usei um código como esse na minha lista de colunas para obter uma lista de matrizes com dois elementos, o COLUMN_NAME, e MAX_LENGTH(documentado no DBIcolumn_info() ).

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

Então eu peguei as exceções INSERTe imprimi algo útil. Neste exemplo, @$rowos dados enviados parasth->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;
}

Além disso, vote e vote novamente na outra resposta

Evan Carroll
fonte
2
Não coloquei nenhuma referência de código sys.columnsporque não tinha absolutamente nenhuma idéia de qual código você está usando no momento para gerar "automagicamente" suas consultas. Realmente não há muito mais complexo que eu possa adivinhar sobre incorporar ao seu código do que SELECT name, object_id, max_length FROM sys.columns;. Como você já possui um código automagic que deve estar fazendo isso - ou algo parecido -, não achei que fosse necessário um exemplo.
Aaron Bertrand
Não sei ao certo como sys.columnsfunciona com duas colunas iguais name. Além disso, comecei a trabalhar com a biblioteca em vez de sys, por que eu faria isso como a resposta escolhida? Microsoft SQL doesn't have x, do y insteadé uma contribuição válida, mas se a sua yfor inferior à minha y, vou fazer algo diferente e marcar como escolhido.
Evan Carroll
1
Sua pergunta era, essencialmente, como descobrir qual coluna estava gerando o erro (presumivelmente, para que você pudesse corrigir esse ponto, em vez de reprojetar a solução). Eu disse para você onde procurar: sys.columns. É exatamente onde você deve comparar os comprimentos das colunas de origem com os comprimentos das colunas de destino. Como você faz isso é com você. Eu não disse a você como corrigir seu código, porque não tenho absolutamente nenhuma idéia de como sua consulta automagica estava sendo gerada, então, como eu disse, não tinha idéia de como adicionar as determinações de comprimento a qualquer consulta que você já tivesse .
Aaron Bertrand
1

Finalmente, a Microsoft decidiu fornecer informações significativas para String or binary would be truncatediniciar a partir do SQL Server 2016 SP2 CU, SQL Server 2017 CU12 e no SQL Server 2019.

As informações agora incluem a coluna da tabela incorreta (nome completo) e o valor incorreto (truncado em 120 caracteres):

A mensagem 2628, nível 16, estado 1, linha x sequência ou dados binários seria truncada na tabela 'TheDb.TheSchema.TheTable', coluna 'TheColumn'. Valor truncado: '...'. A instrução foi encerrada.

Alexei
fonte