Generando múltiples filas de un string (split) en SQL Server

miércoles, 27 de agosto de 2008

Las funciones sp_executesql y execute de t-sql nos permiten generar consultas dinámicamente concadenando textos, algo así se hace generalmente cuando se hace un "select in". Por ejemplo de marcas que tengan unos determinados productos, pero cuyo listado proviene de un conjunto de checks marcados desde una página web.

La consulta antes expuesta debería ser algo así;
select * from brands where brand_id in
(select brand_id from ProductsBrands where product_id in (12,13,14,77,121,598))

Ya que la parte de los identificadores de productos es variante muchos desarrolladores de t-sql optan por concadenar texto y ejecutar sus consultas con los comandos antes mencionados; este tipo de consultas no se pueden optimizar o mejorar mediante las statistics de SQL Server.

declare @listado nvarchar(1000)
set @listado = '10,11,12,13,14,15'

declare @sql nvarchar(1000)
set @sql = 'select * from brands where brand_id in (select brand_id from ProductsBrands where product_id in ( ' + @listado + ' ))'

EXEC(@sql)

Sin embargo existe una posibilidad de generar un conjunto de filas dado un string que contenga todos los identificadores de productos mediante la creación de una función definida por el usuario que devuelva una tabla (es decir un split de un string que devuelva un conjunto de filas con una columna que se pueda utilizar dentro de una "select in" sin necesidad de concadenar textos).


CREATE FUNCTION [dbo].[FnSplitTable]
(@Array varchar(1000),@separator char(1))
RETURNS @table_variable TABLE (col1 nvarchar(1000))
AS
BEGIN
declare @separator_position int
-- almacena el valor de cada vuelta
declare @array_value varchar(1000)
set @array = @array + @separator
-- recorre mientras haya un caracter separador
while patindex('%' + @separator + '%' , @array) <> 0
begin
-- se ubica el separador
set @separator_position = patindex('%' + @separator + '%' , @array)
-- se extrae el valor
set @array_value = substring(@array, 0, @separator_position)
-- se acorta la cadena de caracteres de busqueda
set @array = stuff(@array, 1, @separator_position, '')
-- se almacena en la tabla de respuesta
insert into @table_variable select @array_value as col1
end

RETURN
END



Finalmente nuestra consulta quedaría de la siguiente manera:

declare @listado nvarchar(1000)
set @listado = '10,11,12,13,14,15'


select * from brands
where brand_id in
(select brand_id from ProductsBrands where product_id in (
select col1 from dbo.FnSplitTable(@listado,',')))


Esta consulta si pasa por el proceso de las estadísticas y permite mejorar su rendimiento.


Enlaces relacionados


- FIN -

2 comentarios:

:Dan! dijo...

Excelente!... gracias...

Rene dijo...

Me podrian ayudar ya que quiero hacer de un estring que contiene los store procedure con sus respectivos parametro y retirarlos de dicho string para ejecutarlo... agradeceria su pronta ayuda.