Utilizando Cross apply

Tengo que aceptar que hace mucho tiempo se que existe el Cross Apply (ok, si sé que salió a partir de SQL Server 2005, pero para mis años es mucho tiempo =) ), pero bueno, el caso es que hasta el momento con mayor o menor esfuerzo había podido solventar los problemas que se me presentaban… claro esta que hoy llego un reto mayor.

Un compañero de la oficina, esta haciendo sus primeros pininos en el gran mundo de SQL Server, y entres sus primeras experiencias esta migrando queries y stored procedures hechos en MySql, con lo cual se está enfrentando a todas aquellas cosas de las cuales uno no es conciente hasta que se da de frentazos con ellas.

Un ejemplo, como ya lo he mencionado en SQL Server manejo un Linked Server (Servidor Vinculado) a cada una de las bases de datos en MySQL que viven en cada una de las oficinas a lo largo del país, en cada una de estas bases de datos existe una tabla llamada Clientes, y en esta hay unos campos que se llaman APaterno, AMaterno, Nombre 1 y Nombre 2, donde tenemos como ya se habrán imaginado, el nombre del cliente pero separado.

El caso es que mi compañero hizo un select donde quería obtener algo como: NoCliente, APaterno+ ‘ ‘ + AMaterno + ‘ ‘ + Nombre1 + ‘ ‘ + Nombre2 para entonces insertarlo en una tabla física ya en una base de datos de SQL Server, pero para su sorpresa, el query solo le mostraba algo como:

NoCliente   APaterno

1                       Lopez

Estuvo haciendo varias pruebas y nada funcionaba, el problema que se tiene en este caso, es que en MySql cada uno de los campos que se quiere concatenar estaba declarado como Char(), teníendo entonces el problema de que el tamaño pasa a SQL Server como fijo,  al concatenar los cuatro campos solo alcanzaba a mostrar uno. A este respecto puedo mencionar que un ltrim() o rtrim() no funciono en lo absoluto y que también lo intentamos con substring(campo,lenght(campo)), pero por el paso de información, el lenght() nos devuelve la totalidad de caracteres utilizados, incluyendo los espacios vacios.

Para no hacer el cuento largo, llegamos a la conclusión de que el problema se resuelve si al importar la información casteamos cada campo tipo Char() a un tipo Nvarchar(), sin embargo, teníamos la complicación de que mi compañero tenía un stored procedure dinámico y queríamos mantenerlo de esa manera, sobre todo porque se importan otras tablas y no queríamos escribir todo el select para cada tabla (sobre todo si tomamos en cuenta que la base de datos en MySql no tiene integridad y cada tabla tiene muchisisisisimo campos)

Bueno pues, recordé que contamos con las tablas de sistema y que incluso ya he publicado sobre como obtener los nombres de los campos de una tabla y se me ocurrio que podríamos utilizar esto, sin embargo, para un Create Table, necesitamos también el tipo de dato y de ser necesario el tamaño, luego también necesitariamos poder concatenar todo y ejecutarlo en el stored procedure, cielos!!!

Pues bien, todo esto se resuelve con un Cross Apply, cuya dificultad provenia del mero hecho de que nunca habia usado uno de esos…, pero bien he aqui como lo hemos resuelto mi compañero y yo.

SELECT LEFT(column_names , LEN(column_names )-1) AS column_names
FROM information_schema.columns AS extern
CROSS APPLY
(
SELECT
case when data_type = ‘char’ then ‘cast(‘ + column_name + ‘ as nvarchar(‘+convert(nvarchar(4),character_maximum_length)+’))’+’ ,’  <-Castea aquellas columnas originalmente tipo char
else column_name +’ ,’ end
FROM information_schema.columns AS intern
WHERE extern.table_name = intern.table_name
FOR XML PATH(”)
) pre_trimmed (column_names)
where
table_name = “NombreTabla”  <– Sin esta condición obtendríamos la información de todas las tablas de la base de datos
GROUP BY table_name, column_names

lo anterior se mete a una variable tipo cadena y luego se concatena como sigue,

declare @strSql nvarchar(4000)

set @strSql = ‘select ‘ + @VarCadena + ‘into #TablaTemporal from <<tabla>>’

execute (@strSql)

Como podrán ver, este Cross Apply sirve perfecto para hacer un select into y terminar con nuestra información casteada y 100% utilizable, evitando el problema que describí al comienzo, sin embargo a continuación incluyo un Cross Apply que nos serviría para concatenarlo con un Create Table de ser necesario,

SELECT table_name, LEFT(column_names , LEN(column_names )-1) AS column_names
FROM information_schema.columns AS extern
CROSS APPLY
(
SELECT column_name + ‘ ‘ + case when data_type = ‘char’ then ‘nvarchar’ else data_type end + ‘(‘+convert(nvarchar(4),character_maximum_length)+’)’+’ ,’ <- todos los campos char se crearan como nvarchar()
FROM information_schema.columns AS intern
WHERE extern.table_name = intern.table_name
FOR XML PATH(”)
) pre_trimmed (column_names)
where
table_name = “NombreTabla”  <– Sin esta condición obtendríamos la información de todas las tablas de la base de datos
GROUP BY table_name, column_names;

 

Tengo que reconocer que al final todo funciono diferente para nosotros, ya que si en “NombreTabla” metemos el nombre de la tabla en MySql nos regresa un espantoso NULL, obviamente porque esas tablas no se encuentran registradas en nuestra tabla de sistema =(, taaan triste, pero al final lo resolvimos con una tabla donde metimos algo así como el catálogo de esquemas por tablas y es ahi a donde se irá para saber que campos trabajar por tabla. Sin embargo, puedo pensar en muchos ejemplos donde esto nos resultará realmente util.

Gracias Luis Vela (Mantits) <- veees no olvidé los agradecimientos =)

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s