Evitar el truncado de caracteres en SQL Server

Hola a todos, en esta ocasión vamos a ver cómo evitar el truncado de caracteres que se produce cuando queremos mostrar un resultado por consola o al almacenarlo en un archivo mediante la función PRINT de SQL Server.

Función PRINT

La función PRINT de SQL Server se encarga de mostrar una cadena de mensaje partiendo ya sea de una cadena de caracteres o una constante de cadena Unicode; de una variable de cualquier tipo de datos de caracteres válido o la cuál se pueda convertir a esos tipos de datos; o una expresión que devuelva una cadena.

https://learn.microsoft.com/es-es/sql/t-sql/language-elements/print-transact-sql?view=sql-server-ver16

PRINT msg_str | @local_variable | string_expr 

Sin embargo, esta cadena de mensaje está limitada a 8000 caracteres cuando se trata de una cadena no Unicode o a 4000 caracteres si es una cadena Unicode, por lo que cadenas con una longitud mayor se truncan y no se muestran completas.

Solución mediante la implementación de un buffer de escritura

Para evitar esta limitación tenemos dos opciones posibles. Ambas pasan por crear un buffer que se encargue de recorrer la cadena original e imprimir subcadenas con una longitud máxima de 8000 caracteres hasta haber impreso toda la información.

Además, nos hemos tomado la libertad de incluir dos restricciones a esta solución. Si queremos, se puede indicar una expresión o subcadena de tal forma que se impriman, de los siguientes 8000 caracteres, sólo los anteriores hasta la última aparición de esa expresión. Esto puede resultar útil a la hora de imprimir cadenas que contenga por ejemplo etiquetas XML y no queramos que aparezcan cortadas, ya que por cada impresión de 8000 caracteres se produce un salto de línea.

Evitar el truncado de caracteres

Evitar el truncado de caracteres creando un procedimiento almacenado

En una primera solución, podemos crear un procedimiento en nuestra base de datos que se encargue de este cometido, al cual le pasaremos una serie de parámetros para la impresión de la cadena.

CREATE PROCEDURE dbo.fullPrint
(
@textToPrint VARCHAR(MAX),
@closeKey VARCHAR(20),
@includeKey BIT
)
AS
SET NOCOUNT ON

--Print with buffer (Only 8000 characters can be displayed)
IF (@textToPrint IS NOT NULL)
BEGIN
DECLARE @buffer VARCHAR(8000),
@bufferLength INT = 0,
@startIndex INT = 1,
@closeKeyLength INT,
@lastKeyIndex INT;


IF (@includeKey = 0)
BEGIN
SET @closeKeyLength = LEN(@closeKey)
END;

ELSE
BEGIN
SET @closeKeyLength = 0;
END;


WHILE (1 = 1)
BEGIN
-- The following 8000 characters are selected
SET @buffer = SUBSTRING(@textToPrint, @startIndex, 8000);
SET @bufferLength = LEN(@buffer);
SET @lastKeyIndex = 0;

IF (@bufferLength < 8000)
BEGIN
BREAK;
END;


-- If the substring to be cut is found
IF(CHARINDEX(@closeKey, @buffer)>0)
BEGIN
SET @lastKeyIndex = LEN(@buffer) - LEN(REVERSE(SUBSTRING(REVERSE(@buffer),0,CHARINDEX(REVERSE(@closeKey),REVERSE(@buffer))))) - @closeKeyLength;
END

ELSE
BEGIN
SET @lastKeyIndex = 8000;
END;


-- The contents of the buffer are printed up to the specified index (if it exists).
-- Starts from the next character in the string
IF (@lastKeyIndex > 0)
BEGIN
PRINT SUBSTRING(@buffer, 1, @lastKeyIndex);
SET @startIndex += @lastKeyIndex;
END;

ELSE
BEGIN
PRINT @buffer;
SET @startIndex += @bufferLength;
END
END;

IF (@bufferLength > 0)
BEGIN
PRINT @buffer;
END;
END;
GO

En lugar de la función PRINT se usará esta función pasándole como parámetros:

  • @textToPrint – La cadena a imprimir.
  • @closeKey – Una expresión o subcadena a partir de la cual cortar antes de los 8000 caracteres si fuera necesario.
  • @includeKey – Un valor booleano si queremos que, en caso de encontrar la expresión o subcadena anterior, incluirla antes de cortar o no.

Añadiéndolo a un script

Como segunda solución podemos evitar tener que crear el procedimiento en la base de datos e incluir directamente este bloque de código en nuestro script. Tendremos que establecer el valor de las variables @textToPrint, @closeKey y @includeKey para que funcione correctamente.

SET NOCOUNT ON

DECLARE @textToPrint VARCHAR(MAX),
@closeKey VARCHAR(20),
@includeKey BIT;


--Print with buffer (Only 8000 characters can be displayed)
IF (@textToPrint IS NOT NULL)
BEGIN
DECLARE @buffer VARCHAR(8000),
@bufferLength INT = 0,
@startIndex INT = 1,
@closeKeyLength INT,
@lastKeyIndex INT;


IF (@includeKey = 0)
BEGIN
SET @closeKeyLength = LEN(@closeKey)
END;

ELSE
BEGIN
SET @closeKeyLength = 0;
END;


WHILE (1 = 1)
BEGIN
-- The following 8000 characters are selected
SET @buffer = SUBSTRING(@textToPrint, @startIndex, 8000);
SET @bufferLength = LEN(@buffer);
SET @lastKeyIndex = 0;

IF (@bufferLength < 8000)
BEGIN
BREAK;
END;


-- If the substring to be cut is found
IF(CHARINDEX(@closeKey, @buffer)>0)
BEGIN
SET @lastKeyIndex = LEN(@buffer) - LEN(REVERSE(SUBSTRING(REVERSE(@buffer),0,CHARINDEX(REVERSE(@closeKey),REVERSE(@buffer))))) - @closeKeyLength;
END;
ELSE

BEGIN
SET @lastKeyIndex = 8000;
END;


-- The contents of the buffer are printed up to the specified index (if it exists).
-- Starts from the next character in the string
IF (@lastKeyIndex > 0)
BEGIN
PRINT SUBSTRING(@buffer, 1, @lastKeyIndex);
SET @startIndex += @lastKeyIndex;
END;

ELSE
BEGIN
PRINT @buffer;
SET @startIndex += @bufferLength;
END;
END;

IF (@bufferLength > 0)
BEGIN
PRINT @buffer;
END;
END;

Con estas soluciones podremos imprimir cadenas de cualquier longitud y evitar el truncado de caracteres de la función PRINT.

En caso de estar usando cadenas Unicode se deberá cambiar la longitud máxima del buffer de 8000 a 4000. Esto se podría incluir también en otro parámetro y hacer dinámica la longitud que soporta el buffer. Como veis, la solución cuenta con una gran versatilidad.

Esperamos que os haya resultado interesante y os pueda servir de ayuda. No dudéis en poneros en contacto con nosotros si necesitáis ayuda con la gestión de vuestras bases de datos.

¿Aún no conoces Query Performance? Descubre cómo puede ayudarte en tu entorno Oracle. Más información en su página de LinkedIn.

Sígue a GPS en LinkedIn

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *