Mejorar los errores Data Truncation en SQL Server

Mejorar los errores Data Truncation en SQL Server

Los errores de truncamiento de datos es algo que alguna vez como programadores de SQL Server nos hemos topado.
cuando hablamos de estos errores nos referimos a por ejemplo querer insertar en un VARCHAR(10) la palabra ‘TRIGGERDB CONSULTING SRL‘ lo cual como es lógico nos dará un error.

Ahora bien , este error no es muy detallado que digamos, por ende vamos a ver como podemos trabajar con lo que se denomina “Silent Truncation”

Veamos el siguiente ejemplo:

USE MASTER
GO
DROP DATABASE IF EXISTS TRIGGERDB2019
CREATE DATABASE TRIGGERDB2019
GO

USE TRIGGERDB2019
GO
DROP TABLE IF EXISTS DBO.DEMO_TRUNCATE;

CREATE TABLE DBO.DEMO_TRUNCATE
(ID INT IDENTITY PRIMARY KEY,
 C1 VARCHAR (10));

INSERT INTO DBO.DEMO_TRUNCATE (C1)
VALUES ('TRIGGERDB CONSULTING SRL')
Al ejecutar nuestra operación INSERT observamos el siguiente error

Msg 8152, Level 16, State 30, Line 16
Los datos de cadena o binarios se truncarían.
Se terminó la instrucción.

Msg 8152, Level 16, State 30, Line 1
String or binary data would be truncated.
The statement has been terminated.

SQL Server 2019 

En SQL Server 2019 usando solamente el modo de compatibilidad 150 podremos tener un mejor mensaje de error.

ALTER DATABASE TRIGGERDB2019 
SET COMPATIBILITY_LEVEL = 150

INSERT INTO DBO.DEMO_TRUNCATE (C1)
VALUES ('TRIGGERDB CONSULTING SRL')

Msg 2628, Level 16, State 1, Line 16
Los datos binarios o de la cadena se truncan en la columna “C1” de la tabla “TRIGGERDB2019.dbo.DEMO_TRUNCATE”. Valor truncado: “TRIGGERDB “.
Se terminó la instrucción.

Msg 2628, Level 16, State 1, Line 7
String or binary data would be truncated in table ‘TRIGGERDB2019.dbo.DEMO_TRUNCATE’, column ‘C1’. Truncated value: ‘TRIGGERDB ‘.
The statement has been terminated.

SQL Server 2016 y 2017 

Si se quiere usar esta funcionalidad en SQL Server 2016 o 2017 se requiere activar el Trace Flag 460 el cual esta disponible en el Sp2 de SQL Server 2016 y CU6 de 2017  

TF 460 en el scope
DBCC TRACEON (460)

INSERT INTO DBO.DEMO_TRUNCATE (C1)
VALUES ('TRIGGERDB CONSULTING SRL')

Msg 2628, Level 16, State 1, Line 16
Los datos binarios o de la cadena se truncan en la columna “C1” de la tabla “TRIGGERDB2019.dbo.DEMO_TRUNCATE”. Valor truncado: “TRIGGERDB “.
Se terminó la instrucción.

Msg 2628, Level 16, State 1, Line 7
String or binary data would be truncated in table ‘TRIGGERDB2019.dbo.DEMO_TRUNCATE’, column ‘C1’. Truncated value: ‘TRIGGERDB ‘.
The statement has been terminated.

Mejorar los errores Data Truncation en SQL Server

SQL Server: LEFT JOIN vs NOT EXISTS (Performance)


En mis años como consultor he visto en muchas ocasiones que los programadores usan los JOIN para hacer algún tipo de búsqueda del tipo: Traer los clientes que no tienen ventas o cosas similares.


El JOIN como concepto esta mas pensado para buscar datos entre tablas y presentarlos, por ejemplo si tenemos una tabla facturas y otra clientes entonces es lógico que si necesitamos datos de clientes necesitemos hacer algún tipo de JOIN


En este post veremos el impacto de performance que tiene usar un LEFT JOIN para buscar por ejemplo los clientes que no tienen ordenes en lugar de usar un NOT EXISTS.


Veamos el siguiente ejemplo donde las dos consultas van a retornar los mismos resultados.

USE ADVENTUREWORKS2017 
GO

SELECT * FROM PRODUCTION.PRODUCT P
WHERE NOT EXISTS 
(
 SELECT * FROM SALES.SALESORDERDETAIL D
 WHERE P.PRODUCTID = D.PRODUCTID 
)

SELECT P.* FROM PRODUCTION.PRODUCT P
LEFT JOIN SALES.SALESORDERDETAIL D
ON P.PRODUCTID = D.PRODUCTID 
WHERE D.PRODUCTID IS NULL
En ambas consultas vamos a observar que los resultados son idénticos, retornando 238 registros.

Ahora bien vamos a ver que sucede con sus respectivos planes de ejecución

Podemos observar que en el caso del NOT EXISTS hay una operación eficiente en del índice pero en el LEFT JOIN vemos un SCAN
El costo del primer plan es de 0.10 y el segundo de 0.68 (casi 7 veces mas)

Como clonar una base de datos para resolver problemas de performance

Es una tarea habitual en las distintas organizaciones tener un ambiente productivo donde sucedan problemas de performance y se necesite pasar a un ambiente de testing para determinar sus causas.

Para poder hacer esta tarea lo habitual es hacer una copia de la base de datos en cuestión al otro ambiente para luego trabajar sobre ella y poder hacer el diagnostico correspondiente.

Ahora bien, ¿ que sucede si la base de datos es muy grande? , es probable que actualizar estos ambientes pueda no solo demorar un buen tiempo sino que además consumir recursos.

Las técnicas habituales para pasar estas bases de datos a los ambientes de testing para este tipo de tareas suele ser hacer un Backup y Restore en donde se copia no solo la estructura (schema) sino que ademas todos los datos.

En este post veremos como usar el comando DBCC CLONEDATABASE introducido en el SP2 de SQL 2014.

DBCC CloneDatabase

Este nuevo comando tiene la ventaja que hace un clone de la base de datos pero solo de su estructura, permitiendo así disminuir los tiempos y ademas no consumir los mismos recursos de disco que si hacemos un Backup / Restore.

Ahora bien, ¿como entonces podemos hacer analisis de performance si no tenemos datos?, la respuesta es porque el clonedatabase copia estructuras y estadísticas de la base pero además la deja en un estado de readOnly

Ejemplo

Vamos a ponernos a trabajar y ver un ejemplo en el donde necesitamos analizar unos planes de ejecución para diagnosticar un problema de performance que tenemos en producción.

Tenemos las dos siguientes consultas que generan dos planes de ejecución distintos ante el cambio de un dato

USE ADVENTUREWORKS2014 
GO

SELECT SALESORDERDETAILID, ORDERQTY
FROM SALES.SALESORDERDETAIL
WHERE PRODUCTID = 897;

SELECT SALESORDERDETAILID, ORDERQTY
FROM SALES.SALESORDERDETAIL
WHERE PRODUCTID = 870;

Para replicar este caso en un ambiente de testing vamos a clonar la base de datos (sólo estructuras y estadísticas)

DBCC CLONEDATABASE (ADVENTUREWORKS2014, ADVENTUREWORKS2014_CLONE);    
GO 

Al terminar este comando sobre la instancia vamos a observar el siguiente mensaje

Database cloning for ‘AdventureWorks2014’ has started with target as ‘ADVENTUREWORKS2014CLONE’.
Database cloning for ‘AdventureWorks2014’ has finished. Cloned database is ‘ADVENTUREWORKS2014CLONE’.
Database ‘ADVENTUREWORKS2014CLONE’ is a cloned database. This database should be used for diagnostic purposes only and is not supported for use in a production environment.
DBCC execution completed. If DBCC printed error messages, contact your system administrator.

Si observamos en nuestro SSMS veremos la nueva base de datos en modo ReadOnly y sin datos.

Ahora nos conectaremos a nuestra base de datos clonada para poder replicar la query que corrimos en producción y observaremos que el plan de ejecución es el mismo

USE ADVENTUREWORKS2014CLONE
GO
SELECT SALESORDERDETAILID, ORDERQTY
FROM SALES.SALESORDERDETAIL
WHERE PRODUCTID = 897;
 
SELECT SALESORDERDETAILID, ORDERQTY
FROM SALES.SALESORDERDETAIL
WHERE PRODUCTID = 870;

Conclusiones

El uso de DBCC CloneDatabase puede ayudarnos mucho en este tipo de tareas de análisis de tuning o bien cuando queremos clonar toda una base de datos sin sus datos.

La operación se debe hacer sobre la misma instancia, por lo cual es muy frecuente hacer el clone sobre producción y luego hacer un backup y restore de esa base en tu ambiente de testing.

Esta instrucción además permite clonar datos del Query Store y también backups.