Is there a way in MySQL to do a “round robin” ORDER BY on a particular field in variable universe?

StackOverflow https://stackoverflow.com/questions/22407233

  •  14-06-2023
  •  | 
  •  

Question

My list is a infinite universe, as an example, I would like to take a table such as this one:

+-------+------+
| group | name |
+-------+------+
|     1 | A    |
|     1 | B    |
|     1 | C    |
|     1 | D    |
|     1 | E    |
|     1 | F    |
|     1 | G    |
|     1 | H    |
|     1 | I    |
|     2 | J    |
|     2 | L    |
|     2 | M    |
|     3 | N    |
|     4 | O    |
|     4 | P    |
|     4 | Q    |
|     4 | R    |
|     4 | S    |
|     5 | U    |
|     6 | V    |
+-------+------+

And run a query that produces results in this order:

+-------+------+
| group | name |
+-------+------+
|     1 | A    |
|     2 | J    |
|     3 | N    |
|     4 | O    |
|     5 | U    |
|     6 | V    |
|     1 | B    |
|     2 | L    |
|     4 | P    |
|     1 | C    |
|     2 | M    |
|     4 | Q    |
|     1 | D    |
|     4 | R    |
|     1 | E    |
|     4 | S    |
|     1 | F    |
|     1 | G    |
|     1 | H    |
|     1 | I    |
+-------+------+
Was it helpful?

Solution

select `group`, name from (
  select 
  t.*,
  @rn := if(`group` != @ng, 1, @rn + 1) as ng,
  @ng := `group`
  from t
  , (select @rn:=0, @ng:=null) v
  order by `group`, name
) sq
order by ng, `group`, name

To explain it a little...

  , (select @rn:=0, @ng:=null) v

This line is just a fancy way to initialize the variables on the fly. It's the same as omitting this line but having SET @rn := 0; SET @ng := NULL; before the SELECT.

Then the ORDER BY in the subquery is very important. In a relational database there is no order unless you specify it.

Here

  @rn := if(`group` != @ng, 1, @rn + 1) as ng,
  @ng := `group`

the first line is a simple check, if the value of group in the current row is different from the value of @ng. If yes, assign 1 to @rn, if not, increment @rn.
The order of the columns in the SELECT clause is very important. MySQL processes them one by one. In the second line, we assign the value of group of the current row to @ng. When the next row of the table is processed by the query, in the first line of the two above, @ng will therefore hold the value of the previous row.

The outer select is just cosmetics, to hide unnecessary columns. Feel free to ask if anything is still unclear. Oh, and here you can read more about user defined variables in MySQL.
Note though, that needing variables is rather the exception. They often result in full table scans. Whatever you want to achieve with variables in select statements is often better done on application level, rather than database level.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top