Tablas Temporales vs Variables Tipo Tabla

Desde que era programador pensé que las variables de tipo tabla eran mejores que las tablas temporales porque alguna vez escuché que las primeras eran tablas que vivían en la memoria, mientras que las tablas temporales almacenaban sus datos en disco. Sin embargo, este es un mito que debe ser desterrado ya que la verdad es completamente opuesta y a continuación lo vamos a comprobar.
Sigue leyendo “Tablas Temporales vs Variables Tipo Tabla”

¿Problemas con “Collations”?

No es raro encontrar situaciones en las que un procedimiento almacenado o una consulta funcionaba correctamente en casi todos los ambientes excepto en uno, y normalmente nos encontramos con el siguiente error:

Cannot resolve the collation conflict between “Modern_Spanish_100_CI_AI” and “SQL_Latin1_General_CP1_CI_AS” in the equal to operation.

ira maquina

Este error es producido porque las columnas de condición que se esta usando para un JOIN entre dos tablas son de tipo cadena (CHAR, NCHAR, VARCHAR, NVARCHAR), y el collation de ambas es distinto.

Primero debemos definir que es el “collation”. El “collation” en resumen es una configuracion de servidor, bases de datos y columnas que determinan el comportamiento que tendra la data de tipo caracter con relacion al idioma seleccionado. Para mayor informacion sobre el tema recomiento el siguiente link. Esta configuración no se puede obviar o desaparecer. Para el servidor se determina al momento de la instalacion, asi que si para instalar SQL Server siempre hacer “Next” hasta llegar al final, seguramente instalaste el collation por defecto de SQL Server. Para las bases de datos se determina al momento de crearlas, si no se coloco valor, heredaran la del servidor. Y finalmente la de las columnas se determinan al momento de crear la tabla o agregar nuevas columnas a una tabla existente, si no se determino valor, heredará la de la base de datos. Como se puede observar todo es una catarata de configuraciones que si no se planean correctamente pueden causar problemas mas adelante.

Regresando al tema, en este caso yo tengo instalado una instancia de base de datos con el collation configurado en “Modern_Spanish_100_CI_AI”, mientras que tengo una base de datos configurada en collation “SQL_Latin1_General_CP1_CI_AS”. Ahora dentro de mi base de datos de usuario tengo una tabla a la cual quiero unir con un JOIN a otra tabla temporal que contiene algunos valores, y la quiero unir usado la columna comun entre ambas, la cual es “Codigo”, sin embargo como el collation entre el servidor y la base de datos es diferente me sale el error indicado mas arriba. Para poder reproducir el escenario se puede utilizar el siguiente codigo:

-- verificamos collation del servidor
SELECT SERVERPROPERTY('Collation') AS Collation
/*
Collation
------------------------
Modern_Spanish_100_CI_AI
*/

-- creamos la base de datos con un collation distinto al de la instancia
CREATE DATABASE CollationTest  COLLATE SQL_Latin1_General_CP1_CI_AS

USE CollationTest
GO
-- creamos la tabla
CREATE TABLE TablaA
	(Codigo varchar(30) not null
	,Nombre varchar(255) not null
	,CONSTRAINT pk_TablaA PRIMARY KEY (Codigo))

-- insertamos alguna data de prueba
INSERT INTO TablaA
select 'COD' + CAST(object_id AS VARCHAR(27)), name
from sys.objects

-- creamos una tabla temporal
CREATE TABLE #tmp_tablaA
(Codigo varchar(30) not null
,Nombre varchar(255) not null)

-- agregamos algunos registros a la temporal
insert into #tmp_tablaA
select top 5*
from TablaA
order by 1 desc

-- hacemos una consulta uniendo ambas tablas, y se
-- generará el error
select *
from TablaA a
inner join #tmp_tablaA t on a.Codigo = t.Codigo

Ahora que pudimos generar el error, hay que entender porque se produjo. En este caso el error se debe a que al momento de crear la tabla temporal, esta se crea por defecto con el “collation” definido en la instancia de base de datos al momento de instalarse, es por eso que los “collation” de ambas tablas son distintos.

Hay 2 soluciones a este problema, la primera es la mas rápida, y es que al momento de crear la tabla temporal, explicitamente se le indique el “collation” que usará para sus columnas con el objetivo de que sea el mismo de la base de datos usuario. Mas que una solución permanente es un “workaround” al problema ya que no lo estamos resolviendo sino mas bien tapando, ya que ese mismo patrón de código puede aparecer en otras partes de la aplicación o de los procedimientos almacenados, y tendriamos el mismo problema. Para aplicarla usaremos el siguiente código:

USE CollationTest
GO
-- creamos la tabla temporal con collation explicito
CREATE TABLE #tmp_tablaA
(Codigo varchar(30) COLLATE SQL_Latin1_General_CP1_CI_AS not null
,Nombre varchar(255) COLLATE SQL_Latin1_General_CP1_CI_AS not null )

-- insertamos data de prueba a la temporal
insert into #tmp_tablaA
select top 5*
from TablaA
order by 1 desc

-- hacemos el join, y esta vez sin error
select *
from TablaA a
inner join #tmp_tablaA t on a.Codigo = t.Codigo

La segunda solución es más permanente y considero que es la que se debería seguir, sin embargo requiere de mas tiempo y cuidado para implementarla. Lo que haremos en esta segunda solución es cambiar el “collation” de la instancia para que sea igual al de la base de datos. Normalmente todos las instancias donde esta  una misma base de datos deben tener el mismo “collation” configurado, sin embargo esto no siempre es así. Esta segunda solución propuesta la desarrollare en detalle en otro post.

Espero les sea util la información incluida en este post. Nos vemos. Hasta la próxima.

Tempdb: Reducir Tamaño

Bienvenidos nuevamente, este post es sobre un tema que seguramente muchos de ustedes han tenido que enfrentar, y es que hay ocaciones en las cuales  uno se encuentra con que el disco del servidor que aloja nuestra “tempdb” se encuentra casi llego y algunos procesos que demandan gran espacio en ella se caen justamente por falta de espacio. Sigue leyendo “Tempdb: Reducir Tamaño”