Skip to content
Advertisement

Append Row To Each Group in SQL

Let’s say that I have database table:

| id         | value     | rank        |
| -----------|-----------|-------------|
| 303        | D         | 3           |
| 404        | A         | 1           |
| 505        | B         | 1           |
| 505        | D         | 4           |
| 202        | B         | 1           |
| 505        | A         | 5           |
| 303        | N         | 2           |
| 101        | A         | 1           |
| 505        | A         | 7           |
| 202        | A         | 6           |
| 202        | N         | 3           |
| 505        | N         | 3           |
| 202        | A         | 4           |
| 505        | A         | 2           |
| 202        | N         | 5           |
| 303        | A         | 1           |
| 505        | N         | 6           |
| 202        | A         | 2           |

Following:

SELECT *
FROM table_name
GROUP BY id
ORDER BY rank;

I get:

| id         | value     | rank        |
| -----------|-----------|-------------|
| 101        | A         | 1           |
| 202        | B         | 1           |
| 202        | A         | 2           |
| 202        | N         | 3           |
| 202        | A         | 4           |
| 202        | N         | 5           |
| 202        | A         | 6           |
| 303        | A         | 1           |
| 303        | N         | 2           |
| 303        | D         | 3           |
| 404        | A         | 1           |
| 505        | B         | 1           |
| 505        | A         | 2           |
| 505        | N         | 3           |
| 505        | D         | 4           |
| 505        | A         | 5           |
| 505        | N         | 6           |
| 505        | A         | 7           |

However, for each group, I’d like to append an additional row with the value column taken from the id column so that the resulting table looks like:

| id         | value     | rank        |
| -----------|-----------|-------------|
| 101        | A         | 1           |
| 101        | 101       | 2           |
| 202        | B         | 1           |
| 202        | A         | 2           |
| 202        | N         | 3           |
| 202        | A         | 4           |
| 202        | N         | 5           |
| 202        | A         | 6           |
| 202        | 202       | 7           |
| 303        | A         | 1           |
| 303        | N         | 2           |
| 303        | D         | 3           |
| 303        | 303       | 4           |
| 404        | A         | 1           |
| 404        | 404       | 2           |
| 505        | B         | 1           |
| 505        | A         | 2           |
| 505        | N         | 3           |
| 505        | D         | 4           |
| 505        | A         | 5           |
| 505        | N         | 6           |
| 505        | A         | 7           |
| 505        | 505       | 8           |

What is ANSI SQL (or most database agnostic) way to accomplish this?

Advertisement

Answer

You don’t want a group by in the initial set as you appear to want all the rows back:

select "id", "value", "rank"
from T
union all
select "id", cast("id" as varchar(10)), max("rank") + 1
from T
group by "id"
order by "id", "rank";

And you can do this with grouping sets for the fun of it:

select "id",
    grouping("rank"),
    case when grouping("rank") = 0 then min("value") else cast("id" as varchar(10)) end as "value",
    case when grouping("rank") = 0 then "rank" else max("rank") over (partition by "id") + 1 end as "rank"
from T
group by grouping sets ("id", "rank"), ("id")
order by "id", "rank";
User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement