on
Concatenating MySQL Results with Group_concat and Concat_ws
Recently, I needed to build a query that would transform data in our database into a format that we posted to ElasticSearch. I’ll use the example of blog posts here since they’re easy for everyone to grasp. Imagine that each post can have many tags and you want one field on ElasticSearch with the tag ids and another field that has the tag names and description.
Here’s what the database might look like:
posts
id | title
---|------------------------------
1 | Blogging about dogs is fun!
2 | Look at these cute dogs
3 | Wow how about these cute dogs
post_tags
post_id | tag_id
--------|-------
1 | 1
1 | 2
2 | 1
2 | 3
3 | 1
tags
id | name | description
---|------|-----------------
1 | dogs | Posts about dogs
2 | cute | Cute things
3 | omg | OMG CUTE
And here is the result we’re looking for:
id | title | tag_ids | tag_names_descriptions
---|-----------------------------|---------|------------------------------------------
1 | Blogging about dogs is fun! | 1, 2 | dogs, cute, Posts about dogs, Cute things
Starting Query
Let’s start by retrieving a join table of our posts and tags. We’ll limit this to one post to keep our tables small for this example.
SELECT post.id,
post.title,
tag.id AS tag_id,
tag.name AS tag_name,
tag.description AS tag_description
FROM posts post
INNER JOIN posts_tags pt
ON pt.post_id = post.id
INNER JOIN tags tag
ON tag.id = pt.tag_id
LIMIT 1;
id | title | tag_id | tag_name | tag_description
---|-----------------------------|--------|----------|-----------------
1 | Blogging about dogs is fun! | 1 | dogs | Posts about dogs
2 | Blogging about dogs is fun! | 2 | cute | Cute things
Ok, great, we have a table with our post and its tags, but we have duplicate rows! We can use GROUP BY
to group these row by the post id, but then we’ll lose the tag data in the second row and get a result that looks like this:
id | title | tag_id | tag_name | tag_description
---|-----------------------------|--------|----------|-----------------
1 | Blogging about dogs is fun! | 1 | dogs | Posts about dogs
Group_concat
We can use the Group_concat
function to concatenate data from multiple rows when we use GROUP BY
:
SELECT post.id,
post.title,
Group_concat(tag.id) AS tag_ids,
tag.name AS tag_name,
tag.description AS tag_description
FROM posts post
INNER JOIN posts_tags pt
ON pt.post_id = post.id
INNER JOIN tags tag
ON tag.id = pt.tag_id
GROUP BY post.id
LIMIT 1;
id | title | tag_ids | tag_name | tag_description
---|-----------------------------|---------|----------|-----------------
1 | Blogging about dogs is fun! | 1, 2 | dogs | Posts about dogs
Take a look at the MySQL Docs for Group_concat you can do some cool things with it like ensure the values are unique, sort the values, and choose a custom separator.
But what about our tag_name and tag_description? Here, we need to concatenate two separate columns into one!
Concat_ws
We know that Group_concat
gives us a string, so if we look under String Functions in the MySQL docs, we’ll find Concat_ws
. Concat_ws
lets us concatenate two or more strings with a separator between them. The separator is a comma by default, but for legibility, we want a space too, so let’s use the SEPARATOR
option. Here are our steps:
Concatenate the tag name:
Group_concat(tag.name SEPARATOR ", ")
Concatenate the tag description:
Group_concat(tag.description SEPARATOR ", ")
And concatenate both of those!
Concat_ws(
", "
Group_concat(tag.name SEPARATOR ", "),
Group_concat(tag.description SEPARATOR ", ")
)
Putting it all together
SELECT post.id,
post.title,
Group_concat(tag.id) AS tag_ids,
Concat_ws(
", "
Group_concat(tag.name SEPARATOR ", "),
Group_concat(tag.description SEPARATOR ", ")
) AS tag_names_descriptions
FROM posts post
INNER JOIN posts_tags pt
ON pt.post_id = post.id
INNER JOIN tags tag
ON tag.id = pt.tag_id
GROUP BY post.id
LIMIT 1;
id | title | tag_ids | tag_name | tag_descriptions
---|-----------------------------|---------|------------|------------------------------
1 | Blogging about dogs is fun! | 1, 2 | dogs, cute | Posts about dogs, Cute things
Nice work!
In this post, we’ve learned how to use Group_concat
to concatenate data in a column when using GROUP BY
. Group_concat
lets us keep data that would otherwise disappear when we group things.
We also learned how to combine data from multiple columns by using Concat_ws
together with Group_concat
. With this function, we can join multiple columns and rows together into one value.