Częstą potrzebą korzystania z bazy danych jest uzyskiwanie dostępu do rekordów w kolejności. Na przykład, jeśli mam blog, chcę móc zmieniać kolejność moich postów na blogu w dowolnej kolejności. Wpisy te często mają wiele relacji, więc relacyjna baza danych wydaje się mieć sens.
Typowym rozwiązaniem, które widziałem, jest dodanie kolumny liczb całkowitych order
:
CREATE TABLE AS your_table (id, title, sort_order)
AS VALUES
(0, 'Lorem ipsum', 3),
(1, 'Dolor sit', 2),
(2, 'Amet, consect', 0),
(3, 'Elit fusce', 1);
Następnie możemy posortować wiersze, order
aby uporządkować je we właściwej kolejności.
Jednak wydaje się to niezdarne:
- Jeśli chcę przenieść rekord 0 na początek, muszę zmienić kolejność każdego rekordu
- Jeśli chcę wstawić nowy rekord na środku, muszę zmienić kolejność każdego rekordu po nim
- Jeśli chcę usunąć rekord, muszę zmienić kolejność każdego rekordu po nim
Łatwo jest wyobrazić sobie takie sytuacje, jak:
- Dwie płyty mają to samo
order
order
Pomiędzy rekordami występują luki
Może się to zdarzyć dość łatwo z wielu powodów.
Takie podejście przyjmują aplikacje takie jak Joomla:
Można argumentować, że tutaj interfejs jest zły i że zamiast ludzi bezpośrednio edytujących liczby, powinni używać strzałek lub przeciągania i upuszczania - i prawdopodobnie masz rację. Ale za kulisami dzieje się to samo.
Niektóre osoby zaproponowały użycie miejsca dziesiętnego do przechowywania zamówienia, dzięki czemu można użyć „2,5”, aby wstawić rekord między rekordami w kolejności 2 i 3. I choć to trochę pomaga, jest to prawdopodobnie jeszcze bardziej bałagan, ponieważ możesz skończyć z dziwne miejsca po przecinku (gdzie się zatrzymujesz? 2,75? 2,875? 2,8125?)
Czy istnieje lepszy sposób na przechowywanie zamówień w tabeli?
źródło
orders
i ddl.Odpowiedzi:
Nie, jest prostszy sposób.
To prawda, chyba że użyjesz typu danych, który obsługuje wartości „pomiędzy”. Typy zmiennoprzecinkowe i numeryczne pozwalają zaktualizować wartość, powiedzmy, do 2,5. Ale varchar (n) też działa. (Pomyśl „a”, „b”, „c”; następnie pomyśl „ba”, „bb”, „bc”.)
Nie, jest prostszy sposób. Po prostu usuń wiersz. Pozostałe wiersze nadal będą poprawnie sortowane.
Unikalne ograniczenie może temu zapobiec.
Luki nie mają wpływu na sposób sortowania wartości w kolumnie przez dbms.
Nie przestajesz, dopóki nie musisz . W dbms nie ma problemu z sortowaniem wartości, które mają 2, 7 lub 15 miejsc po przecinku.
Myślę, że twoim prawdziwym problemem jest to, że chciałbyś widzieć wartości w posortowanej kolejności jako liczby całkowite. Możesz to zrobić.
źródło
with cte as (select *,row_number() over (order by sort_order desc) as row from test) update cte set sort_order=row;
To bardzo proste. Musisz mieć strukturę „dziury liczności”:
Musisz mieć 2 kolumny:
integer
bigint
( niedouble
)Wstaw / aktualizuj
order = round(max_bigint / 2)
.order = round("order of first record" / 2)
order = round("max_bigint - order of last record" / 2)
4) Wstawiając na środku ustaworder = round("order of record before - order of record after" / 2)
Ta metoda ma bardzo dużą liczność. Jeśli masz błąd ograniczenia lub uważasz, że masz niewielką liczność, możesz odbudować kolumnę zamówienia (normalizować).
W maksymalnej sytuacji z normalizacją (z tą strukturą) możesz mieć „otwór kardynalności” w 32 bitach.
Pamiętaj, aby nie używać typów zmiennoprzecinkowych - kolejność musi być dokładną wartością!
źródło
Ogólnie rzecz biorąc, zamawianie odbywa się zgodnie z pewnymi informacjami zawartymi w aktach, tytule, dowodzie osobistym lub czymkolwiek odpowiednim dla danej sytuacji.
Jeśli potrzebujesz specjalnego zamówienia, użycie kolumny liczb całkowitych nie jest tak złe, jak mogłoby się wydawać. Na przykład, aby zrobić miejsce na płytę zajmującą 5. miejsce, możesz zrobić coś takiego:
update table_1 set place = place + 1 where place > 5
.Mamy nadzieję, że możesz zadeklarować kolumnę
unique
i być może mieć procedurę, dzięki której przegrupowania będą „atomowe”. Szczegóły zależą od systemu, ale taki jest ogólny pomysł.źródło
Kogo to obchodzi? Liczby te są dostępne tylko dla komputera, więc nie ma znaczenia, ile cyfr ułamkowych mają i jak brzydko nam się wydają.
Użycie wartości dziesiętnych oznacza, że aby przenieść element F między elementami J i K, wystarczy wybrać wartości porządkowe dla J i K, a następnie uśrednić je, a następnie zaktualizować F. Dwie instrukcje SELECT i jedna instrukcja UPDATE (prawdopodobnie wykonano to przy użyciu izolacji możliwej do serializacji, aby uniknąć zakleszczenia).
Jeśli chcesz zobaczyć na wyjściu liczby całkowite, a nie ułamki, oblicz wartości całkowite w aplikacji klienta lub użyj funkcji ROW_NUMBER () lub RANK () (jeśli zawiera je RDBMS).
źródło
W moim projekcie planuję wypróbować rozwiązanie podobne do rozwiązania liczb dziesiętnych, ale zamiast tego użyć tablic bajtowych:
Chodzi o to, że nigdy nie możesz zabraknąć możliwych wartości pośrednich, ponieważ po prostu dołączasz znak
b"\x00"
do odpowiednich rekordów, jeśli potrzebujesz więcej wartości. (nieint
ma ograniczeń w Pythonie 3, w przeciwnym razie musiałbyś wybrać kawałek bajtów na końcu, aby porównać, przy założeniu, że między dwiema sąsiednimi wartościami różnice byłyby upakowane pod koniec.)Na przykład, że masz dwa rekordy,
b"\x00"
ab"\x01"
, a chcesz rekord przejść między nimi. Nie ma żadnych dostępnych wartości między0x00
i0x01
, więc dołączasz jeb"\x00"
do obu, a teraz masz kilka wartości między nimi, których możesz użyć do wstawienia nowych wartości.Baza danych może łatwo to posortować, ponieważ wszystko kończy się w porządku leksykograficznym. Jeśli usuniesz rekord, nadal jest w porządku. W moim projekcie utworzyłem
b"\x00"
ib"\xff"
jakoFIRST
orazLAST
rekordy, aby użyć ich jako wirtualnych wartości „od” i „do” do dodania / dodania nowych rekordów:źródło
Znalazłem tę odpowiedź znacznie lepiej. Cytując to w całości:
źródło