EXPLAIN EXTENDED

How to create fast database queries

Numbering groups

Comments enabled. I *really* need your comment

From Stack Overflow:

I have the following table:

Id Value
1 3
1 12
1 67
2 7
2 99
5 30
5 33
5 4
5 87
5 12
5 1

I'd like to update it:

Id UniqueIdBySubSet Value
1 1 3
1 2 12
1 3 67
2 1 7
2 2 99
5 1 30
5 2 33
5 3 4
5 4 87
5 5 12
5 6 1

Does anyone know how can I do that in MySQL?

These kinds of things are easily done with ROW_NUMBER() in systems that support it.

This function can be emulated in MySQL too. Some time ago I wrote an article on how to do that:

This task, however, is a little more complex. Since we update a single field, we need to change two variables at once in one SET clause.

We cannot use JOIN's here too since we don't yet have a PRIMARY KEY to join on.

However, session variable support in MySQL is flexible enough to do that.

Let's create the table and see how do we do it:

CREATE TABLE t_sequence (
        Id INT NOT NULL,
        UniqueIdBySubSet INT,
        Value INT NOT NULL
) ENGINE=InnoDB;

INSERT
INTO    t_sequence (Id, Value)
VALUES
(1, 3),
(1, 12),
(1, 67),
(2, 7),
(2, 99),
(5, 30),
(5, 33),
(5, 4),
(5, 87),
(5, 12),
(5, 1);

UniqueIdBySubSet is not filled yet:

SELECT  *
FROM    t_sequence
ORDER BY
        id, value

Id UniqueIdBySubSet Value
1 3
1 12
1 67
2 7
2 99
5 1
5 4
5 12
5 30
5 33
5 87

To emulate ROW_NUMBER, we need two variables here: the first one, @id will indicate the id change; the second, @r, will be the counter.

We will need to increment @r whenever @id remains the same, and reset it to 1 when @id changes.

Here's one way to do it:

SET @r := 1;
SET @id := 0;
UPDATE  t_sequence s
SET     s.uniqueidbysubset = IF(@id = id, @r := @r + 1, @r := (@id := id) - id + 1)
ORDER BY
        id, value

If the previous value of id (stored in @id) is equal to the current one, we increment @r.

If it's not, we both assign new value to @id and use the return value of the assignment to reset @r.

Here's what we get after the UPDATE completes:

SELECT  *
FROM    t_sequence
ORDER BY
        id, value

Id UniqueIdBySubSet Value
1 1 3
1 2 12
1 3 67
2 1 7
2 2 99
5 1 1
5 2 4
5 3 12
5 4 30
5 5 33
5 6 87

We now have a sequential set of numbers inside each group.

Written by Quassnoi

June 1st, 2009 at 11:00 pm

Posted in MySQL

Leave a Reply