2008-12-01 9 views
63

Zastanawiam się, czy ktoś ma dobre rozwiązanie problemu, z którym wiele razy się spotkałem w ciągu ostatnich lat.Najlepszy sposób na zapisanie uporządkowanej listy w bazie danych przy zachowaniu zamawiania

Mam koszyk, a mój klient wyraźnie żąda, aby jego zamówienie było znaczące. Więc muszę utrzymać zlecenie w DB.

Oczywistym sposobem byłoby po prostu wstawienie jakiegoś pola OrderField, do którego przypisałbym numer 0 do N i posortowanie go w ten sposób.

Ale to spowodowałoby, że zmiana kolejności będzie trudniejsza i jakoś czuję, że to rozwiązanie jest trochę kruche i pewnego dnia wróci do mnie.

(używam C# 3,5 z NHibernate i SQL Server 2005)

dziękuję

Odpowiedz

22

FWIW, myślę, że tak, jak sugerują (tj popełnienia kolejności do bazy danych) nie jest złym rozwiązaniem do twojego problemu. Myślę też, że to prawdopodobnie najbezpieczniejszy/najbardziej niezawodny sposób.

+1

Czuję, że utrzymanie tej kolejności może powodować bóle głowy w przyszłości. Zapytałem więc i może ktoś znajdzie lepsze rozwiązanie. – Tigraine

+13

Nie martw się - nie ma lepszego sposobu niż użycie kolumny zamawiania. Co do przyszłych bólów głowy ... nie staraj się optymalizować pod kątem przypadków użycia, o których nie wiesz. –

+1

Ponieważ zazwyczaj pobierasz cały wózek do celów wystawowych, numeracja całego koszyka nie wydaje się być problemem. –

0

Polecam zachować odstępy w numerze zamówienia, więc zamiast 1,2,3 itd. Użyj 10,20,30 ... Jeśli musisz po prostu wstawić jeszcze jeden przedmiot, możesz umieścić go na 15, zamiast zmieniać kolejność wszystkiego w tym momencie.

+20

To byłoby tylko OK, jeśli napisałeś system w BASIC ;-) – belugabob

+10

Poważnie, jednak to podejście tylko opóźniłoby potrzebę zrobienia sortowania, a następnie masz dwa różne elementy funkcjonalności w kodzie. Jeśli prawdopodobnie będziesz musiał poradzić sobie z jakimś problemem, dlaczego nie po prostu zrobić to na początku i utrzymać poziom złożoności? – belugabob

5

Niestety nie ma na to magicznej kuli. Nie można zagwarantować kolejności dowolnej instrukcji SELECT BEZ klauzuli order by. Musisz dodać kolumnę i program dookoła niej.

Nie wiem, czy zalecałabym dodawanie przerw w sekwencji zamówień, w zależności od wielkości list i trafień na stronie, możesz zyskać bardzo niewiele dla nadrzędnego zarządzania logiką (ty Nadal potrzebujemy przygotować się na okazję, w której wszystkie luki zostały wykorzystane). Przyjrzałbym się uważnie, jakie korzyści dałoby ci to w twojej sytuacji.

Niestety nie mogę zaoferować nic lepszego, Hope to pomogło.

+2

Ponieważ zazwyczaj pobierasz cały wózek do celów wystawowych, numeracja całego koszyka nie wydaje się być problemem. –

+1

Jeśli lista była na tyle duża, że ​​nie chcesz aktualizować każdego wiersza, gdy coś zmienisz, być może będziesz mógł użyć kolumny DisplayOrder zmiennoprzecinkowej. Nie próbowałem tego, ale to tylko pomysł ... –

1

Właśnie wstawiłbym pole zamówienia. To najprostszy sposób. Jeśli klient może zmienić kolejność pól lub musisz wstawić w środku, po prostu przepisz pola zamówień dla wszystkich pozycji w tej partii.

Jeśli w dół linii znajduje się to ograniczenie ze względu na słabą wydajność wkładek i aktualizacji, wówczas można użyć pola varchar zamiast liczby całkowitej. Pozwala to na dość wysoki poziom precyzji podczas wstawiania. np. wstawić między pozycjami "A" i "B" można wstawić element zamówiony jako "AA". Jest to prawie na pewno przesada w przypadku koszyka na zakupy.

+0

Co jest po "Z" w twoim sortowaniu pola varchar? – cdmckay

0

Cóż, powiedziałbym, krótka odpowiedź brzmi:

Utwórz podstawowy klucz autoidentity w tabeli cartcontents, a następnie wstawić wiersze w odpowiedniej kolejności od góry do dołu. Następnie wybierając z tabeli według porządku kolumna autosugestii klucza głównego wyświetli tę samą listę. Robiąc to, musisz usunąć wszystkie przedmioty i ponownie wstawić je w przypadku zmian zawartości koszyka. (Ale to wciąż jest całkiem czysty sposób robienia tego) Jeśli nie jest to możliwe, przejdź do kolumny zamówienia, jak sugerują inni.

3

Nie polecam w ogóle podejścia A, AA, B, BA, BB. Istnieje wiele dodatkowych procesów związanych z ustalaniem hierarchii i wstawianie wpisów pomiędzy nimi nie jest wcale zabawne.

Wystarczy dodać OrderField, integer.Nie używaj luk, ponieważ wtedy musisz albo pracować z niestandardowym "krokiem" na następnej środkowej wstawce, albo będziesz musiał najpierw ponownie zsynchronizować listę, a następnie dodać nowy wpis.

Posiadanie 0 ... N jest łatwe do zmiany kolejności i jeśli możesz użyć metod Array lub metod listy poza SQL, aby zmienić kolejność kolekcji jako całości, a następnie zaktualizować każdy wpis, lub możesz dowiedzieć się, gdzie wstawiają się do, a +1 lub -1 do każdego wejścia odpowiednio przed lub przed nim.

Gdy masz już do tego napisaną bibliotekę, będzie to bułka z masłem.

1

Na poziomie abstrakcji powyżej koszyka Przedmioty powiedzmy, że CartOrder (który ma 1-sz CartItem), możesz zachować pole o nazwie itemOrder, które może być po prostu oddzieloną przecinkami listą id (PK) rekordów cartItem istotnych . Będzie to warstwa aplikacji, którą musisz przeanalizować i odpowiednio ustawić modele przedmiotów. Dużym plusem dla tego podejścia będzie zmiana kolejności zamówień, może nie być zmian w poszczególnych obiektach, ale ponieważ porządek jest utrzymywany jako pole indeksu w rzędach tabeli zamówień, trzeba będzie wydać polecenie aktualizacji dla każdego z nich. wiersze aktualizujące swoje pole indeksu. Proszę dać mi znać swoją krytykę tego podejścia, jestem ciekawy, w jaki sposób może to zawodzić.

9

Co powiesz na wykorzystanie implementacji listy powiązanej? Posiadanie jednej kolumny będzie zawierać wartość (numer porządkowy) następnego elementu. Myślę, że jest to zdecydowanie najłatwiejsze w użyciu podczas wstawiania zamówień pomiędzy. Nie trzeba przenosić numeru.

30

Najlepsze rozwiązanie to Doubly Linked list. O (1) dla wszystkich operacji z wyjątkiem indeksowania. Nic nie może indeksować SQL szybko, ale z wyjątkiem klauzuli where elementu, który chcesz.

0,1020 typów nie powiodło się. Sekwencyjne kolumny nie działają. Kolumna sekwencji przestawnej kończy się niepowodzeniem przy ruchach grupowych.

Lista podwójnie połączona to ta sama operacja dodawania, usuwania, usuwania grupy, dodawania grupy, przenoszenia grupowego. Pojedyncza połączona lista działa również. Podwójnie połączone jest jednak lepsze z SQL. Pojedyncza połączona lista wymaga posiadania całej listy.

+3

To jest najlepsze rozwiązanie IMHO. Na przykład mam więcej niż 300 filmów w mojej kolejce Netflix. Jeśli przeniesię element z pozycji 297 na górę, netflix nie będzie musiał wykonywać 300 aktualizacji. Tylko 3 aktualizacje w najlepszym wypadku – user35559

+6

Jaki jest najlepszy sposób na zdobycie pierwszych 50 przedmiotów? Czy muszę czytać je jeden po drugim, czy istnieje jakieś magiczne rozwiązanie sql? – akn

+0

@akn Byłoby to możliwe za pomocą kwerendy cyklicznej, a następnie ograniczenie liczby wierszy o 50, aby uzyskać pierwsze 50 wierszy. –

46

Ok jest tutaj moje rozwiązanie, aby ułatwić programowanie każdemu, kto dzieje się w tym wątku. Sztuką jest możliwość aktualizacji wszystkich indeksów zamówień powyżej lub poniżej wstawiania/usuwania w jednej aktualizacji.

Używając numerycznej (integer) kolumny w tabeli, wspierany przez zapytań SQL

CREATE TABLE myitems (Myitem TEXT, id INTEGER PRIMARY KEY, orderindex NUMERIC); 

Aby usunąć element w orderindex 6:

DELETE FROM myitems WHERE orderindex=6;  
UPDATE myitems SET orderindex = (orderindex - 1) WHERE orderindex > 6; 

Aby zamienić dwa egzemplarze (4 i 7):

UPDATE myitems SET orderindex = 0 WHERE orderindex = 4; 
UPDATE myitems SET orderindex = 4 WHERE orderindex = 7; 
UPDATE myitems SET orderindex = 7 WHERE orderindex = 0; 

tj. 0 nie jest używane, więc użyj go jako manekina, aby uniknąć posiadania niejednoznacznego przedmiotu.

Aby wstawić na 3:

UPDATE myitems SET orderindex = (orderindex + 1) WHERE orderindex > 2; 
INSERT INTO myitems (Myitem,orderindex) values ("MytxtitemHere",3) 
-1

Gdy używam Hibernate i trzeba zapisać rzędu @OneToMany używam Map a nie List.

@OneToMany(fetch = FetchType.EAGER, mappedBy = "rule", cascade = CascadeType.ALL) 
@MapKey(name = "position") 
@OrderBy("position") 
private Map<Integer, RuleAction> actions    = LazyMap.decorate(new LinkedHashMap<>(), FactoryUtils.instantiateFactory(RuleAction.class, new Class[] { Rule.class }, new Object[] { this })); 

W tym przykładzie Java position jest własnością Integer z RuleAction więc kolejność jest utrwalane w taki sposób. Sądzę, że w języku C# będzie to wyglądać podobnie.

1

Rozwiązałem to pragmatycznie tak:

  1. Kolejność jest zdefiniowane w interfejsie użytkownika.

  2. Moduł zaplecza otrzymuje żądanie POST zawierające identyfikatory i odpowiednią pozycję każdego elementu na liście.

  3. Rozpoczynam transakcję i aktualizuję pozycję dla każdego identyfikatora.

Gotowe.

Zamawianie jest drogie, ale przeczytanie uporządkowanej listy jest bardzo tanie.