Reprezentuj kolejność w relacyjnej bazie danych

Mam zbiór obiektów w bazie danych. Zdjęcia w galerii zdjęć, produkty w katalogu, rozdziały w książce itp. Każdy obiekt jest reprezentowany jako wiersz. Chcę móc dowolnie zamawiać te obrazy, przechowując je w bazie danych, aby podczas wyświetlania obiektów były we właściwej kolejności.

Na przykład, powiedzmy, że piszę książkę, a każdy rozdział jest przedmiotem. Piszę książkę, a rozdziały umieszczam w następujących kolejność:

Wprowadzenie, dostępność, forma a funkcja, błędy, spójność, wniosek, indeks

Idzie do Redakcji i wraca z następującą sugerowaną kolejnością:

Wprowadzenie, Forma, Funkcja, Dostępność, Spójność, Błędy, Wnioski, Indeks

Jak Mogę przechowywać to zamówienie w bazie danych w solidny i wydajny sposób?

Miałem następujące pomysły, ale nie jestem zachwycony żadnym z oni:

  1. Array. Każdy wiersz ma identyfikator zamówienia, gdy zamówienie jest zmieniane (poprzez usunięcie, a następnie wstawienie), identyfikatory zamówień są aktualizowane. To sprawia, że pobieranie jest łatwe, ponieważ jest po prostu ORDER BY, ale wydaje się łatwe do złamania.

    // REMOVAL
    UPDATE ... SET orderingID=NULL WHERE orderingID=removedID
    UPDATE ... SET orderingID=orderingID-1 WHERE orderingID > removedID
    // INSERTION
    UPDATE ... SET orderingID=orderingID+1 WHERE orderingID > insertionID
    UPDATE ... SET orderID=insertionID WHERE ID=addedID

  2. Linked list. Każdy wiersz ma kolumnę identyfikującą następny wiersz w zamówieniu. Trawersowanie wydaje się tu kosztowne, choć może w jakiś sposób wykorzystać ORDER BY o którym nie myślę.

  3. / align = "left" / Ustaw orderingID (jak w #1) na duży, więc pierwszy obiekt to 100, drugi to 200, itd. Następnie, gdy zachodzi wstawianie, po prostu umieszczasz je w (objectBefore + objectAfter)/2. Oczywiście od czasu do czasu musiałoby to zostać zrównoważone, więc nie masz rzeczy zbyt blisko siebie (nawet w przypadku pływaków, w końcu napotkasz błędy zaokrąglania).

Żadne z nich nie wydaje mi się szczególnie eleganckie. Czy ktoś ma lepszy sposób żeby to zrobić?
Author: tghw, 2008-08-21

11 answers

Inną alternatywą byłoby (jeśli twój RDBMS go obsługuje) użycie kolumn typu array. Chociaż łamie to Zasady normalizacji, może być przydatne w takich sytuacjach. Jedną z baz danych, o której Wiem, że ma tablice, jest PostgreSQL.

 7
Author: Grey Panther,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-22 05:32:46

Mixin acts_as_list w Rails obsługuje to w zasadzie tak, jak opisałeś w #1. Szuka kolumny o nazwie position (której możesz zastąpić nazwą oczywiście) i używa jej do wykonania polecenia przez. Jeśli chcesz ponownie zamówić rzeczy, zaktualizuj pozycje. Służył mi dobrze za każdym razem, gdy go używałem.

Na marginesie, możesz usunąć konieczność zmiany pozycjonowania na wstawianie / usuwanie za pomocą rzadkiej numeracji -- coś w stylu basic z powrotem w dzień... ty możesz numerować swoje pozycje 10, 20, 30 itp. a jeśli chcesz wstawić coś pomiędzy 10 a 20, po prostu wstawiaj to z pozycją 15. Podobnie podczas usuwania możesz po prostu usunąć wiersz i zostawić lukę. Musisz tylko zrobić ponowne numerowanie, gdy faktycznie zmienisz kolejność lub jeśli spróbujesz zrobić wkładkę i nie ma odpowiedniej przerwy do wstawienia.

Oczywiście w zależności od konkretnej sytuacji (np. czy inne wiersze są już załadowane do pamięci czy nie) to zastosowanie podejścia gap może, ale nie musi mieć sensu.

 4
Author: John,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 23:11:17

Tylko myśl rozważająca Opcja #1 vs #3: czy opcja spaced array (#3) nie odkłada tylko problemu normalnej tablicy (#1)? Niezależnie od algorytmu, który wybierzesz, albo jest zepsuty, i napotkasz problemy z #3 później, albo działa, a następnie #1 powinien działać równie dobrze.

 3
Author: onnodb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-25 17:24:13

Jeśli obiekty nie są mocno zaznaczone przez inne tabele, a listy są krótkie, usuwanie wszystkiego w domenie i ponowne wstawianie odpowiedniej listy jest najłatwiejsze. Ale to nie jest praktyczne, jeśli listy są duże i masz wiele ograniczeń, aby spowolnić usuwanie. Myślę, że twoja pierwsza metoda jest naprawdę najczystsza. Jeśli uruchomisz go w transakcji, możesz być pewien, że nic dziwnego się nie dzieje, gdy jesteś w środku aktualizacji, aby spieprzyć zamówienie.

 2
Author: Eric Z Beard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-22 01:39:15

Zrobiłem to w moim ostatnim projekcie, ale było to dla tabeli, która tylko od czasu do czasu musiała być specjalnie zamówiona, i nie była dostępna zbyt często. Myślę, że spaced array byłby najlepszym rozwiązaniem, ponieważ zmiana kolejności byłaby najtańsza w przeciętnym przypadku, tylko z udziałem zmiany na jedną wartość i zapytanie na dwóch).

Również, wyobrażam sobie, że ORDER BY byłby dość mocno zoptymalizowany przez dostawców baz danych, więc wykorzystanie tej funkcji byłoby korzystne dla wydajności, a nie do wdrożenia linked list.

 2
Author: pbh101,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-22 01:58:14

Użyj liczby zmiennoprzecinkowej do reprezentowania pozycji każdego elementu:

Pozycja 1 - > 0.0

Pozycja 2 - > 1.0

Pozycja 3 - > 2.0

Pozycja 4 - > 3.0

Możesz umieścić dowolny element pomiędzy dowolnymi dwoma innymi elementami poprzez proste bisekcja:

Pozycja 1 - > 0.0

Pozycja 4 - > 0.5

Pozycja 2 - > 1.0

Pozycja 3 - > 2.0

(przesunięto poz. 4 między poz. 1 i 2).

Proces bisekcji może trwać niemal w nieskończoność ze względu na sposób unoszenia liczby punktów są kodowane w systemie komputerowym.

Pozycja 4 - > 0.5

Pozycja 1 - > 0.75

Pozycja 2 - > 1.0

Pozycja 3 - > 2.0

(Przesuń pozycję 1 do pozycji zaraz po pozycji 4)

 2
Author: Seun Osewa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 00:22:32

Zrobiłbym kolejny numer, z wyzwalaczem na stole, który "robi miejsce" dla priorytetu, jeśli już istnieje.

 1
Author: Stu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-21 23:12:30

Ja też miałem ten problem. Byłem pod dużą presją czasu (czyż nie wszyscy) i poszedłem z opcją # 1, i tylko zaktualizowane wiersze, które się zmieniły.

Jeśli zamienisz pozycję 1 na pozycję 10, po prostu wykonaj dwie aktualizacje, aby zaktualizować numery porządkowe pozycji 1 i pozycji 10. Wiem, że jest to algorytmicznie proste i jest to O(N) najgorszy przypadek, ale ten najgorszy przypadek jest wtedy, gdy masz całkowitą permutację listy. Jak często to się zdarza? To twoja odpowiedź.

 1
Author: moffdub,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-18 00:34:31

Ponieważ głównie natknąłem się na to z Django, uznałem To rozwiązanie za najbardziej wykonalne. Wydaje się, że nie ma żadnego "właściwego sposobu", aby to zrobić w relacyjnej bazie danych.

 1
Author: tghw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-03-29 14:47:15

Miałem ten sam problem i prawdopodobnie spędziłem co najmniej tydzień na temat właściwego modelowania danych, ale myślę, że w końcu go mam. Używając typu danych tablicy w PostgreSQL, możesz zapisać klucz główny każdego zamówionego elementu i odpowiednio zaktualizować tablicę za pomocą wstawiania lub usuwania, gdy twoje zamówienie ulegnie zmianie. Odwoływanie się do jednego wiersza pozwoli Ci zmapować wszystkie obiekty na podstawie kolejności w kolumnie tablica.

To jeszcze trochę niepewne rozwiązanie, ale to prawdopodobnie będzie działać lepiej niż opcja # 1, ponieważ opcja 1 wymaga aktualizacji numeru zamówienia wszystkich innych wierszy podczas zamawiania zmian.

 0
Author: Austin T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-28 10:32:16

Schemat # 1 i schemat #3 mają tę samą złożoność w każdej operacji z wyjątkiem INSERT zapisów. Schemat # 1 ma zapis O(n) na INSERT, a Schemat #3 ma zapis O(1) na INSERT.

Dla każdej innej operacji bazy danych złożoność jest taka sama.

Schemat # 2 nie powinien być nawet brany pod uwagę, ponieważ jego DELETE wymaga odczytu i zapisu O(n). Schemat # 1 i schemat #3 mają O (1) DELETE zarówno dla odczytu, jak i zapisu.

Nowa metoda

Jeśli twoje elementy mają odrębny element nadrzędny (tj. mają wspólny obcy wiersz klucza), następnie możesz spróbować następujących czynności ...

Django oferuje niezależne od bazy danych rozwiązanie do przechowywania list liczb całkowitych w CharField(). Jedną z wad jest to, że maksymalna długość przechowywanego ciągu nie może być większa niż max_length, która jest zależna od DB.

Pod względem złożoności, dałoby to Schemat # 1 o(1) zapisuje dla INSERT, ponieważ informacja o kolejności byłaby przechowywana jako pojedyncze pole w wierszu elementu nadrzędnego.

[[9]}kolejną wadą jest to, że JOIN do wiersza nadrzędnego jest teraz wymagane, aby zaktualizować kolejność.

Https://docs.djangoproject.com/en/dev/ref/validators/#django.core.validators.validate_comma_separated_integer_list

 0
Author: Chris Conlan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-15 15:04:28