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 -

asp:repeater OnItemCommand no funciona (no se ejecuta)

jueves, 21 de agosto de 2008

Durante la creación de un formulario de búsqueda, con filtros listados en forma de grid, además claro de los resultados; utilicé un control Repeater con un UpdatePanel para recargarlos usando Ajax en ambos casos.

El problema:
El evento OnItemCommand del asp:repeater no se ejecutaba nunca.

Escenario:
En el Page_Load se cargaba los datos del Repeater únicamente en la primera carga (!Page.IsPostBack)

La solución:
El hecho es que para que este evento se lanze, el control Repeater debe tener datos, asi que la carga del Repeater se ha de hacer siempre en el evento Page_Load independientemente si es postback o no; si aún asi, no se desea esto, la otra solución es activar el viewstate del updatepanel.

<asp:UpdatePanel ID="updatePanelFiltro" EnableViewState="true" runat="server" >

No esta demás recalcar que los eventos a controlar asíncronamente han de estar dentro de la sección de Triggers del UpdatePanel llamado en este caso updatePanelFiltro.


<asp:AsyncPostBackTrigger ControlID="RepeaterFiltro" EventName="ItemCommand" />


- FIN -

HTML Entities - Encoding / Decoding

miércoles, 20 de agosto de 2008

Desde la página asp.net disponemos de dos métodos para codificar y descodificar un texto:

  • Server.HtmlEncode("canción")
  • Server.HtmlDecode("canci&oacute;n")
Desde javascript, para decodificar un texto con las entidades Html codificadas no existe ninguna función por defecto, por tanto se puede usar la siguiente función :


function DecodeHtmlEntities(str){
try{
var txt=document.createElement('textarea');
txt.innerHTML = str;
return txt.value;
}catch(e){
return str;
}
}

Aunque por otro lado javascript dispone de las funciones escape y unescape para codificar/descodificar urls.

Enlaces relacionados:

- FIN -

Llamadas dinámicas(callbacks) dentro de un asp:Repeater

jueves, 14 de agosto de 2008

Hay ocasiones en las que deseamos presentar un listado de elementos, utilizando un control Repeater y poder por ejemplo eliminar un elemento del listado, independiente del resto a través de su identificador. El siguiente texto explica como realizar esto.

La página a de implementar la interfaz IPostBackEventHandler


public partial class MiPaginaASPX : System.Web.UI.Page, IPostBackEventHandler {


Esta interfaz nos obliga a implementar el método RaisePostBackEvent, este será el que atienda las llamadas desde el cliente; recibe un parámetro en forma de cadena de texto (en este caso el identificador del registro).


public void RaisePostBackEvent(string eventArgument){
//lógica de negocio....
DatosManager.BorrarRegistro(eventArgument);
//lógica de negocio....
}

Finalmente para generar el código javascript que llame al evento RaisePostBackEvent de la página se ha de utilizar el método GetPostBackEventReference disponible dentro del ClientScript.

public string getRemoveImage(object obj) {
ShortView r = (ShortView)obj;
string evento = Page.ClientScript.GetPostBackEventReference(this, r.Id);
string res = "<a href=\"#\" onclick=\"" + evento + "\"><img src=\"/Resources/icons/borrar.png\" border=\"0\" /></a>";
return res;
}

La llamada al método getRemoveImage se realiza desde dentro del ItemTemplate:

....
<td><%# getRemoveImage(Container.DataItem)%></td>
......

No esta demás decir que esta es solo una de las posibles soluciones.

- FIN -