Wstęp
Mamy przykładową tabelę z userami:
DROP TABLE users; CREATE TABLE users( id SERIAL PRIMARY KEY, name VARCHAR(50), created TIMESTAMP DEFAULT NOW() ); INSERT INTO users(name) VALUES('Marcin'); INSERT INTO users(name) VALUES('Bartek'); INSERT INTO users(name) VALUES('Agnieszka'); INSERT INTO users(name) VALUES('Dominika');
Na tabeli wykonywane były wielokrotnie operacje INSERT/UPDATE/DELETE przykładowo:
UPDATE users SET name = 'Ania' WHERE name = 'Bartek'; UPDATE users SET name = 'Dalia' WHERE name = 'Marcin'; DELETE FROM users WHERE name = 'Ania';
Zatem SELECT bez orderu wyświetli nam dane posortowane względem kolejności modyfikacji:
SELECT * FROM users; psql=> select * from users; id | name | created ----+-----------+---------------------------- 3 | Agnieszka | 2009-12-19 10:41:49.324851 4 | Dominika | 2009-12-19 10:41:49.958792 1 | Dalia | 2009-12-19 10:41:49.297223 (3 rows)
W interface aplikacji chcemy oprogramować własną obsługę kolejności rekordów. Dodajemy zatem kolumnę, która będzie obsługiwać tą funkcjonalność.
ALTER TABLE users ADD COLUMN order_id INTEGER;
Problem
Musimy teraz uzupełnić kolumne order_id o poprawne wartości. Kolejność orderu danych, które znajdują się w bazie powinna być kolejności wprowadzania tych danych do tabeli (czyli zgodna z kolejnością pola id).
Aby zrealizować poprawne uzupełnienie orderu tworzymy pomocniczą sekwencję, która wykorzystamy do wygenerowania kolejnych liczb do orderu:
DROP SEQUENCE order_id_seq; CREATE SEQUENCE order_id_seq;
Wykonujemy oczywiste zapytanie przypisujące rekordom z tabeli users kolejne wartości sekwencji:
UPDATE users SET order_id = NEXTVAL('order_id_seq');
Niestety przez to, że dane nie były ustawione w dobrej kolejności ORDER ustawiony jest niepoprawnie.
psql=> SELECT * FROM users; id | name | created | order_id ----+-----------+----------------------------+---------- 3 | Agnieszka | 2009-12-19 10:41:49.324851 | 1 4 | Dominika | 2009-12-19 10:41:49.958792 | 2 1 | Dalia | 2009-12-19 10:41:49.297223 | 4 (3 rows)
Rozwiązanie
Aby UPDATE poprawnie ustawił kolejność orderu należy nieco zmodyfikować zapytanie i nadać danym z tabeli users odpowiednią kolejność.
W tym celu tworzymy podzapytanie sorted_ids, w którym poprzez ORDER BY ustalamy kolejność rekordów, które następnie w zapytaniu nadrzędnym zostaną zmodyfikowane właśnie w tej kolejności.
UPDATE users SET order_id = correct_order_id FROM ( SELECT id, nextval('order_id_seq') as correct_order_id FROM users ORDER BY id ASC ) as sorted_ids WHERE users.id = sorted_ids.id; select * from users order by order_id;
Jak widać dane zostały pięknie zorderowane:
psql=> select * from users order by order_id; id | name | created | order_id ----+-----------+----------------------------+---------- 1 | Dalia | 2009-12-19 10:41:49.297223 | 1 3 | Agnieszka | 2009-12-19 10:41:49.324851 | 2 4 | Dominika | 2009-12-19 10:41:49.958792 | 3 (3 rows)
Przy okazji wspomnę, że w aplikacji łatwiej obsłużyć ordering odwrotny, tzn. wiersze, które posiadają największą wartość order_id są na czele a nie na końcu listy. Przy takim podejściu przy wstawianiu nowego rekordu pole order_id powinno mieć wartość MAX(order_id) + 1. Dla orderingu odwrotnego należy zmienić w podzapytaniu kolejność sortowania z id ASC na id DESC.
Należy także dbać o to, aby przy usunięciu rekordu prenumerować order_id wszystkich rekordów, które znajdują się 'nad nim’ o -1.