I need to update a table with sequential numbering, but skipping numbers that are already in use.
The table has 3 (important) columns: id, name and order, where order is the display order of the buttons. When empty, it looks like this:
id | some_name | order_number |
---|---|---|
1 | null | 1 |
2 | null | 2 |
3 | null | 3 |
... | ||
10 | null | 10 |
User can fill in names (or leave them blank), but also mess around with the order of the buttons, even the empty ones. After saving the data, I end up with something like this:
id | some_name | order_number |
---|---|---|
1 | null | 6 |
2 | red | 5 |
3 | null | 8 |
4 | null | 9 |
5 | blue | 3 |
6 | null | 4 |
7 | null | 1 |
8 | yellow | 7 |
9 | null | 10 |
10 | null | 2 |
CREATE TABLE my_table(id tinyint(1),order_number tinyint(1),some_name varchar(40));
INSERT INTO my_table (id, order_number, some_name) VALUES
(1,6,NULL),(2,5,"red"),(3,8,NULL),(4,9,NULL),(5,3,"blue"),(6,4,NULL),(7,1,NULL),(8,7,"yellow"),(9,10,NULL),(10,2,NULL);
What I like to do is renumber the order_numbers in such a way that
In the above example buttons 2, 5 and 8 have a name, so I keep their order numbers (5, 3, 7). The other ones should be renumbered from 1 to 10 (ordered by id), skipping the order_numbers in use. In the end the table should look like this:
id | some_name | order_number | (why) | (Next seq number) |
---|---|---|---|---|
1 | ||||
1 | null | 1 | name is empty, use next: 1 | +1 = 2 |
2 | red | 5 | full, keep number 5 | 2 |
3 | null | 2 | empty, use next: 2 | +1 = 3 : used in id=5 -> +1 = 4 |
4 | null | 4 | empty, use next: 4 | +1 = 5 : used in id=2 -> +1 = 6 |
5 | blue | 3 | full, keep number 3 | 6 |
6 | null | 6 | empty, use next: 6 | +1 = 7 : used in id=8 -> +1 = 8 |
7 | null | 8 | empty, use next: 8 | +1 = 9 |
8 | yellow | 7 | full, keep number 7 | 9 |
9 | null | 9 | empty, use next: 9 | +1 = 10 |
10 | null | 10 | empty, use next: 10 | +1 = 11 |
Selecting the id's where name = null is not a problem:
SELECT id FROM my_table WHERE some_name IS NULL
Result: 1, 3, 4, 6, 7, 9, 10 (id's 3, 5, 8 keep their order_number)
Also, selecting the sequential order_numbers not in use:
SELECT NR.new_number
FROM
(
select @ordnum := @ordnum + 1 as new_number
FROM my_table,(select @ordnum := 0) r
ORDER BY new_number LIMIT 10
) NR
LEFT JOIN
(
SELECT order_number FROM my_table
WHERE some_name IS NULL
) RO
ON(RO.order_number = NR.new_number)
WHERE RO.order_number IS NOT NULL
ORDER BY NR.new_number
Result: 1, 2, 4, 6, 8, 9, 10 (numbers 3, 5, 7 are used)
The problem is how to connect these two queries (they have nothing in common except giving a result set of the same length), so that it picks the first id and the first order_number, updates, then picks the second row from each query, updates, and so on. Every kind of JOIN I tried so far either gives me the id's but the sequential numbering is stuck on 1, or it runs every id against every order_number, resulting in every id having order_number = 10.
Solved.
The two queries in my question needed something to connect them in a JOIN. Wrap them both up in a new SELECT, adding a counter to both. Now they can connect on the counter, which is the same for both queries (as both filter on the same thing: rows where name is null).
SELECT
/* result */
empty_names.id,
new_order.new_number
SELECT
@s1:=@s1+1 AS sq,
id
FROM(
/* QUERY 1 goes here */
) AS empty_names
INNER JOIN
(
SELECT
@s2:=@s2+1 AS sq,
Y.new_number
FROM(
/* QUERY 2 goes here */
) Y,
(select @s2:=0) s2
) new_order
/* connect on the counter */
ON empty_names.sq = new_order.sq
query 1 | query 2 | result | |||||
---|---|---|---|---|---|---|---|
seq 1 | id | seq 2 | new number | id | new number | ||
1 | 1 | 1 | 1 | 1 | 1 | ||
2 | 3 | 2 | 2 | 3 | 2 | ||
3 | 4 | 3 | 4 | 4 | 4 | ||
4 | 6 | 4 | 6 | 6 | 6 | ||
5 | 7 | 5 | 8 | 7 | 8 | ||
6 | 9 | 6 | 9 | 9 | 9 | ||
7 | 10 | 7 | 10 | 10 | 10 |
The result now can be used to update the table.