Può l'ordinamento da un'istruzione ORDER BY essere salvato in modo esplicito con una sola istruzione UPDATE?
Domanda
Ho questa tabella:
SELECT * FROM items
id | item | position
---|-----------|----------
1 | USB cable | 0
2 | SD card | 4
3 | Mouse | 2
4 | Keyboard | 0
5 | Monitor | 3
Ordinamento questa tabella dà questo risultato:
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
Ora voglio aggiornare la tabella e salvare l'ordine nella colonna di posizione:
SELECT * FROM items
id | item | position
---|-----------|----------
4 | Keyboard | 1
1 | USB cable | 2
3 | Mouse | 3
5 | Monitor | 4
2 | SD card | 5
Può questo essere fatto con una singola query o devo ciclo manualmente su tutte le righe e fare un aggiornamento manuale?
Nel caso l'ordine non completamente definito (ad esempio per il cavo USB e tastiera sopra) ho arbitrariamente deciso l'ordine.
Soluzione
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
Nota: l'ordine di "tastiera" e "cavo USB" è arbitraria. Entrambi hanno posizione = 0
Per legare-break posizioni basate su articolo, una sorta secondario
ROW_NUMBER() OVER (ORDER BY position, item) AS rn
Se si dispone di posizione duplicato, coppie elemento questo sarà arbitrario troppo ...
Altri suggerimenti
Ecco come raggiungere questo obiettivo 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;
Ho usato i dati di esempio dalla questione, caricato in MySQL 5.5.15 sul mio portatile, e sono corso quelle due righe. Ecco il risultato:
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)