GuilleSQL :: Microsoft SQL Server, SSIS, y más !! Destroy On Sight - Listen Up !!

Acumulado: Pasar múltiples Filas a una Columna, en una consulta SQL sin cursores


Este artículo muestra una problemática algo habitual al desarrollar consultas SQL, el problema de convertir múltiples filas en una única fila (pasar filas a columna) con todos los valores de las anteriores (por ejemplo, separados por punto y coma), una forma de construir un acumulado (no confundir con las funciones PIVOT y UNPIVOT de SQL Server). El motivo por el que he decidido incluir esto como artículo, es porque quiero mostrar como es posible desarrollar dicha consulta SQL y calcular dicho acumulado concatenando todas las filas, SIN CURSORES (como dios manda ;-) a través de un ejemplo.

Aunque mi perfil profesional quizás esté algo más orientado a Técnica de Sistemas que a Desarrollo, debo admitir que el mundo del desarrollo siempre me ha picado, y aunque he hecho mis pinitos, tengo esa espina clavada de que me habría gustado hacer algo más (bueno... mucho más). En cualquier caso, no es la primera vez que escribo sobre asuntos más propios de desarrolladores (como la depuración de procedimientos almacenados, UDF y Triggers, Cómo trabajar con Fechas en SQL Server, SQL Injection, Claves Subrogadas, Tablas Versionadas, etc.), y es que la realidad de la realidad, es que para un Administrador de Base de Datos es de gran utilidad conocer con claridad ciertos conceptos de desarrolladores, que en la práctica pueden ser de mucha ayuda para optimizar sistemas de bases de datos, ofrecer un buen soporte a desarrollo, etc.

Continuando con el tema que nos ocupa, en esta ocasión vamos a realizar esta exposición basándonos en un ejemplo. Imaginemos que tenemos una tabla USUARIOS, que contiene un campo EMAIL, y se nos solicita que desarrollemos una consulta SQL (o un procedimiento almacenado, lo que sea) para obtener todas las direcciones de correo electrónico separadas por punto y coma.

La forma habitual de resolver esto para la mayoría de los programadores es iterando, lo cual en el desarrollo de base de datos, implica crear un cursor (algo así como un RecordSet para los programadores de ADO) para poder recorrer cada fila, y en una variable poder ir calculando nuestro valor acumulado (ya sea concatenando, o aplicando la fórmula que fuere).

En el caso particular de SQL Server, la utilización de cursores es una práctica poco recomendable, lo mismo que ocurre con Sybase (recordemos que SQL Server viene de Sybase, y que ambos motores han estado compartiendo código varios años), con IBM Informix, y con otros muchos motores de base de datos.

Quizás sea que la mayoría de los programadores están más tiempo desarrollando otras partes de la aplicación (ej: formularios) que la propia base de datos, y sea por esto por lo que se adquieran ciertas costumbres (como es el hecho de Iterar, y en consecuencia, de utilizar Cursores como método natural de trabajar).

Sin embargo, la realidad es que es posible desarrollar todo (o prácticamente todo) sin necesidad de utilizar Cursores, como ocurre en el caso de este ejemplo (pasar filas a columna), aunque quizás no sea la forma más natural de realizarlo (en un principio, que al final, lo de evitar cursores es como montar en bici ;-).

Sin más misterio, aprovecho para incluir un trozo de código Transact-SQL con la consulta SQL solicitada. Principalmente, existen dos trucos:

  • El primer truco consiste en declarar una variable que utilizaremos para almacenar el valor Acumulado, en el caso del ejemplo, la variable @AcumuladoEmails.
  • El segundo truco consiste en utilizar dicha variable (@AcumuladoEmails) en la sentencia SELECT para calcular nuestro valor acumulado, que en nuestro caso es concatenar direcciones de correo electrónico separadas por punto y coma. Para esto, hemos utilizado la función COALESCE, con el objetivo de que para la primera fila se tome sólo el EMAIL, y para las siguientes filas se mote el EMAIL precedido del punto y coma.

Por último, podremos realizar la acción que deseemos con nuestra variable de acumulado (en el ejemplo @AcumuladoEmails), como por ejemplo seleccionarla (es decir, mostrarla como parte de una sentencia SELECT), insertarla en una tabla, etc.

Aquí va el código:

DECLARE @AcumuladoEmails VARCHAR(max)

SELECT @AcumuladoEmails = COALESCE(@AcumuladoEmails + '; ' + EMAIL, EMAIL)
FROM dbo.USUARIOS
WHERE USUARIO_ACTIVO = 1
ORDER BY EMAIL

SELECT @AcumuladoEmails

Como podréis comprobar, esta consulta SQL funciona, es rápida, y no utiliza cursores. Si estuviésemos trabajando con una tabla USUARIOS muy grande (ej: con muchos millones de filas), habría que ver si esta tarea sería recomendable hacerla en la base de datos o fuera de la base de datos, para sacar el mayor rendimiento del sistema, y teniendo en cuenta que el caso que nos ocupa se trata de una acción intensiva en concatenaciones (recordemos la existencia de la clase StringBuilder de Net Framework, el dios de las concatenaciones).

Y poco más. Sólo quería compartir este truquillo para quienes les pueda interesar, y también para mí, que tengo memoria de pez, y seguro que la próxima vez que me haga falta, se me ha olvidado... jeje ;-)


[Fecha del Artículo (UTC): 26/04/2009]
[Autor: GuilleSQL]


Miembros de
Miembros de GITCA (Global IT Community Association)

Menu de Usuario
  Iniciar Sesión
  Registrarse
  Restablecer Contraseña
  Ventajas de Registrarse

Acerca de
  Contigo desde Nov 2007
  187 usuarios registrados
  57069 pageloads/mes
  Ranking Alexa 1092744



Archivo

Julio de 2010 (3)
Mayo de 2010 (5)
Abril de 2010 (6)
Marzo de 2010 (8)
Febrero de 2010 (3)
Enero de 2010 (1)
Diciembre de 2009 (9)
Noviembre de 2009 (14)
Octubre de 2009 (2)
Septiembre de 2009 (8)
Agosto de 2009 (2)
Julio de 2009 (10)
Junio de 2009 (8)
Mayo de 2009 (9)
Abril de 2009 (9)
Marzo de 2009 (2)
Febrero de 2009 (1)
Enero de 2009 (2)
Noviembre de 2008 (2)
Octubre de 2008 (2)
Septiembre de 2008 (1)
Agosto de 2008 (4)
Julio de 2008 (5)
Mayo de 2008 (3)
Abril de 2008 (2)
Marzo de 2008 (1)
Febrero de 2008 (2)
Enero de 2008 (3)
Noviembre de 2007 (2)
Octubre de 2007 (1)






Esta información se proporciona "como está" sin garantías de ninguna clase, y no otorga ningún derecho.
This information is provided "AS IS" with no warranties, and confers no rights.

Copyright © 2007 GuilleSQL, todos los derechos reservados.
GuilleSQL.com y GuilleSQL.net son también parte de Portal GuilleSQL.

Visitas recibidas (Page Loads) en GuilleSQL (fuente: StatCounter):

screen resolution stats
Visitas