Concatenar datos de una columna en una fila
- 3 minutos de lecturaMe he topado en varias ocasiones con la necesidad de crear una vista en la que un campo contiene concatenados el valor de varios registros de otra tabla. Casi siempre he acabado recurriendo a Google para recordar cómo hacerlo, porque, por alguna razón que desconozco, esta información no queda registrada en mi hipocampo.
En este artículo vamos a ver cómo conseguirlo con un ejemplo muy sencillo. Partimos de 3 tablas: autor, libro y libro_autor, que contienen, como podemos suponer, una relación de autores, libros y libro-autor. La estructura de estas tres tablas es la que se muestra en el siguiente diagrama.
Ahora supongamos que queremos mostrar el listado de todos los libros y que en una columna queremos que aparezcan concatenados el nombre de todos los autores del libro. El resultado que queremos obtener es el siguiente:
id_libro | titulo | autores |
---|---|---|
1 | libro1 | autor1, autor2 |
2 | libro2 | autor2, autor1 |
3 | libro3 | autor3 |
La forma más sencilla de obtener este resultado es creando una función que devuelva el nombre de todos los autores, pasando como parámetro el identificador del libro. Esto lo podemos hacer fácilmente haciendo uso de las funciones ISNULL o COALESCE. En este ejemplo, lo conseguimos mediante la función COALESCE.
CREATE FUNCTION dbo.DevuelveActores ( @id\_libro int )
RETURNS VARCHAR(40)
AS
BEGIN
DECLARE @autores VARCHAR(40)
SELECT @autores = COALESCE(@autores + ', ' + nombre, nombre)
FROM libro\_autor la
LEFT JOIN autor a ON la.id\_autor = a.id\_autor
WHERE id\_libro=@id\_libro
RETURN @autores
END
Ahora podemos, mediante la siguiente consulta, obtener el resultado deseado:
SELECT id_libro, titulo, autores = dbo.DevuelveActores(id_libro)
FROM libro
Otra opción es hacer uso del modo PATH de la cláusula FOR XML. Este modo permite definir, mediante los nombres o alias de la columnas, la manera cómo se asignan los valores en el XML, así como el nombre del elemento, pudiendo sobrescribir el valor _
SELECT nombre as \[data()\] FROM autor FOR XML PATH ('')
Haciendo uso de esta característica, es posible montar una consulta con una subconsulta que devuelva todos los autores de un libro y reemplazar el espacio entre autores por el separador que queramos. Con la siguiente consulta obtenemos exactamente el mismo resultado que hemos conseguido antes con la función.
SELECT id_libro, titulo, autores = CAST( REPLACE( (
SELECT nombre as [data()]
FROM libro_autor la
LEFT JOIN autor a ON la.id_autor = a.id_autor
WHERE id_libro = l.id_libro
FOR XML PATH ('')
), ' ', ', ') AS NVARCHAR(40) )
FROM libro l
Dejo un enlace para descargar el script que contiene las consultas de los dos métodos explicados y la creación de todos los elementos de base de datos (tablas y funciones) que he utilizado en este ejemplo.