Take the 2-minute tour ×
Database Administrators Stack Exchange is a question and answer site for database professionals who wish to improve their database skills and learn from others in the community. It's 100% free, no registration required.

I have this table:

SELECT * FROM items

id | item      | position
---|-----------|----------
 1 | USB cable |        0
 2 | SD card   |        4
 3 | Mouse     |        2
 4 | Keyboard  |        0
 5 | Monitor   |        3

Sorting this table gives this result:

SELECT * FROM items ORDER BY position

id | item      | position
---|-----------|----------
 4 | Keyboard  |        0
 1 | USB cable |        0
 3 | Mouse     |        2
 5 | Monitor   |        3
 2 | SD card   |        4

Now I want to update the table and save the order in the position column:

SELECT * FROM items

id | item      | position
---|-----------|----------
 4 | Keyboard  |        1
 1 | USB cable |        2
 3 | Mouse     |        3
 5 | Monitor   |        4
 2 | SD card   |        5

Can this be done with a single query or do I have to manually loop over all rows and do a manual update?

share|improve this question

migrated from stackoverflow.com Aug 28 '11 at 18:47

This question came from our site for professional and enthusiast programmers.

    
"Keyboard" and "USB cable": why one before the other? –  gbn Aug 28 '11 at 17:43
    
In case the order is not defined so I just arbitrarily decided the order to be that way. Of course the DBMS might decide otherwise... –  DR Aug 28 '11 at 17:54
    
@DR: I've added this bit to my answer –  gbn Aug 28 '11 at 17:55
    
Should this quesation be tagged mysql, sql-server, oracle, postgresql, or database-agnostic ? –  RolandoMySQLDBA Aug 28 '11 at 23:08

2 Answers 2

UPDATE
   T1
SET
   position = T2.rn
FROM
   myTable T1
   JOIN
   (
   SELECT
      id,
      ROW_NUMBER() OVER (ORDER BY position) AS rn
   FROM
      myTable
   ) T2 ON T1.id = T2.id

Note: the order of "Keyboard" and "USB cable" is arbitrary. They both have position = 0

To tie-break positions based on item, add a secondary sort

      ROW_NUMBER() OVER (ORDER BY position, item) AS rn

If you have duplicate position,item pairs this will be arbitrary too...

share|improve this answer
1  
+1 for using standard ANSI SQL –  a_horse_with_no_name Aug 29 '11 at 5:01
1  
ROW_NUMBER() OVER (ORDER BY position) I suppose. –  msi77 Aug 29 '11 at 5:30
    
@msi77: oops, corrected. Thank you. –  gbn Aug 29 '11 at 5:50

Here is how to achieve this in MySQL:

set @x = 0;
update items A
inner join
(
    select @x:=@x+1 newposition,id
    from items
    order by position
) B using (id)
set A.position=B.newposition;

I used your sample data from the question, loaded it into MySQL 5.5.15 on my laptop, and ran those two lines. Here is the result:

mysql> use test
Database changed
mysql> drop table if exists items;
Query OK, 0 rows affected (0.01 sec)

mysql> create table items
    -> (
    ->     id int not null auto_increment,
    ->     item varchar(20),
    ->     position int,
    ->     primary key(id)
    -> );
Query OK, 0 rows affected (0.01 sec)

mysql> insert into items (item,position) values
    -> ('USB cable',0),('SD Card',4),
    -> ('Mouse',2),('Keyboard',0),('Monitor',3);
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> select * from items;
+----+-----------+----------+
| id | item      | position |
+----+-----------+----------+
|  1 | USB cable |        0 |
|  2 | SD Card   |        4 |
|  3 | Mouse     |        2 |
|  4 | Keyboard  |        0 |
|  5 | Monitor   |        3 |
+----+-----------+----------+
5 rows in set (0.00 sec)

mysql> select * from items order by position;
+----+-----------+----------+
| id | item      | position |
+----+-----------+----------+
|  1 | USB cable |        0 |
|  4 | Keyboard  |        0 |
|  3 | Mouse     |        2 |
|  5 | Monitor   |        3 |
|  2 | SD Card   |        4 |
+----+-----------+----------+
5 rows in set (0.00 sec)

mysql> set @x = 0;
Query OK, 0 rows affected (0.00 sec)

mysql> update items A
    -> inner join
    -> (
    ->     select @x:=@x+1 newposition,id
    ->     from items
    ->     order by position
    -> ) B using (id)
    -> set A.position=B.newposition;
Query OK, 5 rows affected (0.00 sec)
Rows matched: 5  Changed: 5  Warnings: 0

mysql> select * from items;
+----+-----------+----------+
| id | item      | position |
+----+-----------+----------+
|  1 | USB cable |        1 |
|  2 | SD Card   |        5 |
|  3 | Mouse     |        3 |
|  4 | Keyboard  |        2 |
|  5 | Monitor   |        4 |
+----+-----------+----------+
5 rows in set (0.00 sec)

mysql> select * from items order by position;
+----+-----------+----------+
| id | item      | position |
+----+-----------+----------+
|  1 | USB cable |        1 |
|  4 | Keyboard  |        2 |
|  3 | Mouse     |        3 |
|  5 | Monitor   |        4 |
|  2 | SD Card   |        5 |
+----+-----------+----------+
5 rows in set (0.00 sec)
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.