¿Cómo identifico las columnas responsables de "La cadena o los datos binarios se truncarían".

31

Estoy generando algunas consultas de forma automática con el código que escribí a SELECT desde una base de datos remota de Pg e inserto en una base de datos local de SQL Server. Sin embargo, uno de ellos está generando este error:

[Microsoft] [Controlador ODBC SQL Server] [SQL Server] La cadena o los datos binarios se truncarían. (SQL-22001) [el estado era 22001 ahora 01000]

[Microsoft] [Controlador ODBC SQL Server] [SQL Server] La declaración ha finalizado. (SQL-01000) en. \ Insert.pl línea 106.

¿Cómo averiguo qué columna genera ese error y carece de la longitud para la entrada? ¿Hay alguna manera de hacer esto sin adivinar la fuerza bruta varchar?

Evan Carroll
fuente

Respuestas:

35

No, no está registrado en ningún lado. Vaya a votar y exponga su caso de negocios; Este es uno de la larga lista de cosas que deben corregirse en SQL Server.

Esto se solicitó hace años en Connect (probablemente primero en el marco de tiempo de SQL Server 2000 o 2005), luego nuevamente en el nuevo sistema de comentarios:

Y ahora se ha entregado, en SQL Server 2019 , SQL Server 2017 CU12, y aparecerá en un futuro SQL Server 2016 SP2 CU.

En el primer CTP público de SQL Server 2019, solo aparece bajo la marca de seguimiento 460. Esto suena un poco secreto, pero se publicó en este documento técnico de Microsoft . Este será el comportamiento predeterminado (no se requiere marca de seguimiento) en el futuro, aunque podrá controlar esto a través de una nueva configuración de base de datos VERBOSE_TRUNCATION_WARNINGS.

Aquí hay un ejemplo:

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

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

Resultado en todas las versiones compatibles anteriores a SQL Server 2019:

El mensaje 8152, Nivel 16, Estado 30, Línea 5
Cadena o datos binarios se truncarían.
La instrucción se ha terminado.

Ahora, en los CTP de SQL Server 2019, con el indicador de rastreo habilitado:

DBCC TRACEON(460);
GO

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

El resultado muestra la tabla, la columna y el valor ( truncado , no lleno ):

Msg 2628, Nivel 16, Estado 1, Línea 11 La
cadena o los datos binarios se truncarían en la tabla 'tempdb.dbo.x', columna 'a'. Valor truncado: 'f'.
La instrucción se ha terminado.

Hasta que pueda soltar todo y actualizar a SQL Server 2019, o pasar a la Base de datos SQL de Azure, puede cambiar su código "automático" para extraer la longitud máxima sys.columns, junto con el nombre del que debe estar llegando de todos modos, y luego aplicar LEFT(column, max_length)o cualquiera que sea el equivalente de PG. O, dado que eso solo significa que perderá datos silenciosamente, descubra qué columnas no coinciden y arregle las columnas de destino para que se ajusten a todos los datos de la fuente. Dado el acceso a los metadatos a ambos sistemas, y el hecho de que ya está escribiendo una consulta que debe coincidir automáticamente con las columnas fuente -> destino (de lo contrario, este error no sería su mayor problema), no debería tener que hacer ninguna fuerza bruta adivinando en absoluto.

Aaron Bertrand
fuente
2

Si tiene acceso para ejecutar el Asistente de importación y exportación de SQL Server desde SQL Server Management Studio (haga clic con el botón derecho en la base de datos> Tareas> Importar datos ...), cree una tarea que importe desde SQL Client utilizando su consulta como origen de datos al destino mesa.

Antes de ejecutar la importación, puede revisar la asignación de datos y le dirá qué columnas tienen tipos de campo inconsistentes. Y si ejecuta la tarea de importación, le indicará qué columna (s) no se pudo importar.

Advertencia de validación de muestra:

Advertencia 0x802092a7: Flujo de datos Tarea 1: El truncamiento puede ocurrir debido a la inserción de datos de la columna de flujo de datos "NARRATIVA" con una longitud de 316 a la columna de la base de datos "NARRATIVA" con una longitud de 60. (Asistente de importación y exportación de SQL Server)

bubbassauro
fuente
1

Finalmente, no pude encontrar una manera de obtener la información de la columna sin escribirla yo mismo.

Este mensaje de error fue generado por DBD::ODBC, pero también puedes usarlo sys.columns (max_length)(simplemente no sé cómo).

Utilicé un código como este en mi lista de columnas para obtener una lista de matrices con dos elementos, el COLUMN_NAMEy MAX_LENGTH(documentado en DBIcolumn_info() ).

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

Luego capté las excepciones INSERTe imprimí algo útil. En este ejemplo, @$rowlos datos enviados asth->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;
}

Además, vote y vote a favor la otra respuesta

Evan Carroll
fuente
2
No puse ninguna referencia de código sys.columnsporque no tenía ni idea de qué código está usando actualmente para generar "automáticamente" sus consultas. Realmente no hay mucho más complejo que podría adivinar sobre incorporar a su código que SELECT name, object_id, max_length FROM sys.columns;. Como ya tiene un código automático que debe estar haciendo esto, o algo parecido, no pensé que fuera necesario un ejemplo.
Aaron Bertrand
No estoy seguro de cómo sys.columnsfunciona con dos columnas que tienen lo mismo name. Además, conseguí que la cosa funcionara usando la biblioteca en lugar de sys, ¿por qué haría eso como la respuesta elegida? Microsoft SQL doesn't have x, do y insteades una contribución válida, pero si yes inferior a la mía y, voy a hacer algo diferente y marcarlo como elegido.
Evan Carroll
1
Su pregunta fue, esencialmente, ¿cómo puedo averiguar qué columna estaba generando el error (presumiblemente, para que pueda solucionar ese punto, en lugar de rediseñar la solución). Te dije dónde buscar: sys.columns. Que es exactamente donde debe buscar para comparar las longitudes de columna de origen con las longitudes de columna de destino. Cómo lo haces depende de ti. No le dije cómo arreglar su código, porque no tengo ni idea de cómo se estaba generando su consulta automática en primer lugar, así que, como dije, no tenía idea de cómo agregar las determinaciones de longitud a la consulta que ya tenía .
Aaron Bertrand
1

Finalmente, Microsoft decidió proporcionar información significativa para String or binary would be truncatedcomenzar desde SQL Server 2016 SP2 CU, SQL Server 2017 CU12 y en SQL Server 2019.

La información ahora incluye tanto la columna de la tabla ofensiva (nombre completo) como el valor ofensivo (truncado a 120 caracteres):

El mensaje 2628, Nivel 16, Estado 1, Línea x Cadena o datos binarios se truncarían en la tabla 'TheDb.TheSchema.TheTable', columna 'TheColumn'. Valor truncado: '...'. La instrucción se ha terminado.

Alexei
fuente