2011-12-22 12 views
26

Załóżmy, że mam tabelę Product w bazie danych witryny handlowej, aby zachować opis, cenę itp. Produktów sklepu. Jaki jest najskuteczniejszy sposób, aby mój klient był w stanie ponownie zamówić te produkty?Korzystanie z kolumny sortowania w tabeli bazy danych

Tworzę kolumnę Order (liczbę całkowitą) używaną do sortowania rekordów, ale to daje mi pewne bóle głowy dotyczące wydajności ze względu na prymitywne metody, których używam do zmiany kolejności każdego rekordu po tym, który faktycznie muszę zmienić. Przykład:

Id Order 
5  3 
8  1 
26 2 
32 5 
120 4 

co teraz mogę zrobić, aby zmienić kolejność zapisu z ID=26 do 3?

To, co zrobiłem, to utworzenie procedury, która sprawdza, czy istnieje rekord w kolejności docelowej (3) i aktualizuje kolejność wierszy (ID = 26), jeśli nie. Jeśli istnieje rekord w kolejności docelowej, procedura wykonuje się wysyłając identyfikator tego wiersza z target order + 1 jako parametry.

To powoduje zaktualizować każdy pojedynczy rekord po jednej Chcę zmienić, aby zrobić miejsce:

Id Order 
5  4 
8  1 
26 3 
32 6 
120 5 

Więc co mądrzejszy człowiek zrobić?

  • Używam SQL Server 2008 R2.

Edit:

muszę kolumnę kolejność pozycji na tyle do sortowania bez kluczy wtórnych zaangażowanych. Sama kolumna zamówienia musi określać unikalne miejsce dla jej rekordu.

Poza tym zastanawiam się, czy mogę zaimplementować coś w rodzaju połączonej listy: kolumnę "Dalej" zamiast kolumny "Zamówienie", aby zachować identyfikator następnego elementu. Ale nie mam pojęcia, jak napisać zapytanie, które pobiera zapisy z prawidłową kolejnością. Jeśli ktoś ma pomysł na temat tego podejścia, proszę podziel się.

+12

Bardziej doświadczona osoba nie nazwałbym kolumny 'Order' ponieważ to już zarezerwowane słów kluczowych w SQL .... :-) –

Odpowiedz

24
Update product set order = order+1 where order >= @value changed 

Choć z czasem dostaniesz coraz większe „przestrzeni” w porządku, ale to będzie nadal „sort”

Spowoduje to dodanie 1 do wartości ulega zmianie, a każda wartość po nim w jednym oświadczenie, ale powyższe stwierdzenie jest nadal prawdziwe. większe i większe "przestrzenie" utworzą w Państwa zamówieniu możliwość osiągnięcia wartości INT.

rozwiązanie alternatywne podane pragnienie bez spacji:

Wyobraź procedurę: UpdateSortOrder z parametrami @NewOrderVal @ IDToChange @ OriginalOrderVal kroku proces

Dwa zależności czy nowy/stary porządek porusza się w górę lub w dół ten rodzaj.

If @NewOrderVal < @OriginalOrderVal --Moving down chain 

--Create space for the movement; no point in changing the original 
    Update product set order = order+1 
    where order BETWEEN @NewOrderVal and @OriginalOrderVal-1; 

end if 

If @NewOrderVal > @OriginalOrderVal --Moving up chain 

--Create space for the momvement; no point in changing the original 
    Update product set order = order-1 
    where order between @OriginalOrderVal+1 and @NewOrderVal 
end if 

--Finally update the one we moved to correct value 

    update product set order = @newOrderVal where [email protected]; 

Odnośnie najlepszej praktyki; Większość środowisk, w których się znajdowałem, zwykle chce mieć coś pogrupowane według kategorii i posortowane alfabetycznie lub w oparciu o "popularność na wyprzedaży", co zaprzecza potrzebie zdefiniowania przez użytkownika.

+1

Dzięki. Nie jest to doskonałe, ale to by działało i to jest najważniejsze w rozwoju. Ale spodziewałem się czegoś w rodzaju "najlepszej praktyki" w tej sytuacji. Potrzebuję go dość często i za każdym razem wymyślam różne metody. To dla mnie zaskakujące, że nie ma dobrze znanej metody, którą wszyscy znają i używają. –

+0

cóż, użytkownik mógłby kliknąć przycisk "ZROBIONY", który następnie użyłby ustawić pole "Zamówienie" = rownum na podstawie zapytania posortowanego według "zamówienia" – xQbert

+0

To dobre rozwiązanie. Dziękuję Ci. Mam jeszcze jedno pytanie: zaktualizowałeś rekordy pomiędzy, zanim zaktualizowałeś ten, który był potrzebny do zmiany w twoim drugim przykładzie. Oznacza to, że będą na chwilę pojawiać się dwa wiersze o tej samej wartości zamówienia. Jak mogę to zrobić, jeśli mam unikalne ograniczenie w kolumnie Zamówienie? –

4

Jednym z rozwiązań, z których korzystałem w przeszłości, z pewnym powodzeniem, jest użycie "ciężaru" zamiast "zamówienia". Waga jest oczywista, cięższy przedmiot (tj. Niższa liczba) opada na dno, lżejszy (wyższa liczba) wznosi się na samą górę.

W przypadku, gdy mam wiele przedmiotów o tej samej wadze, zakładam, że mają one tę samą wagę i zamawiam je alfabetycznie.

Oznacza to SQL będzie wyglądać następująco:

ORDER BY 'weight', 'itemName' 

nadzieję, że pomoże.

+0

Dziękuję. To rozwiązanie, ale nie różni się od mojej obecnej metody. To moja wina, ponieważ powinienem był sprecyzować, że potrzebuję, aby kolumna zamówienia była unikatowa. (Natychmiast zaktualizuję pytanie) Potrzebuję klienta, aby móc dokładnie wybrać, który przedmiot jest pierwszy. Nie mam warunku wtórnego takiego jak imię, w tym przypadku Zakon musi wystarczyć. Jeśli mam dwie rzeczy ważone 10 i 11, to w jaki sposób mogę sprawić, by rekord pojawił się między nimi bez zmiany ich ciężarów (a w konsekwencji wagi tych, które po nich nastąpiły) było moim prawdziwym pytaniem. –

+0

Czy zamówienie musi być unikalne bez przerw? Jeśli tak, dlaczego? Opisana technika może doskonale mieć unikalne ograniczenie na kolumnie, więc kolejność jest wyjątkowa. –

+0

Niestety, nie rozumiem. Luki nie są problemem. Po prostu nie rozumiem, co się stanie, gdy dwa przedmioty mają taką samą wagę lub jak mogę utworzyć rekord pomiędzy dwoma rekordami ważonymi 10 i 11 (jak wspomniałem w poprzednim komentarzu). Zwłaszcza jeśli mam unikalne ograniczenie na kolumnie. W takim przypadku musiałbym zmienić wagę jednego z pozostałych elementów. Sugerujesz, że nie powinienem zwiększać/zmniejszać ciężaru o 1? Przepraszam, jeśli brakuje tu czegoś oczywistego. –

5

Użyj starej sztuczki używanej przez programy BASIC (między innymi): przeskocz liczby w kolumnie kolejności o 10 lub inny wygodny przyrost. Następnie możesz wstawić pojedynczy wiersz (w rzeczywistości, do 9 wierszy, jeśli masz szczęście) między dwiema istniejącymi liczbami (które są oddalone od siebie o 10). Możesz też przenieść wiersz od 370 do 565 bez zmiany żadnego z wierszy od 570 wzwyż.

1

(przepraszam za angielski.) Studiuję)

Jest bardzo prosty. Trzeba mieć "dziury liczności"

strukturę trzeba mieć 2 kolumny

1) pk = 32bit int

2) obciążenie = 64bit bigint (BIGINT, nie podwójne !!!)

wstaw/zaktualizuj

1) po wstawieniu pierwszego nowego rekordu należy ustawić order = round (max_bigint/2).

2) jeśli włożysz w początku tabeli należy ustawić kolejność = round („Kolejność pierwszej płycie”/2)

3) jeśli włożysz w końcu tabeli należy ustawić kolejność = round („max_bigint - kolejność ostatniej płycie”/2)

4) jeśli włożysz w środku trzeba ustawić kolejność = round („kolejność zapisu zanim - rzędu rekord po”/2)

tej metody ma wielką wielką karditalność. jeśli masz błąd ograniczenia lub jeśli uważasz, że masz małą liczność, możesz odbudować kolumnę zamówienia (znormalizować).

w sytuacji maksymalności z normalizacją (z tą strukturą) można mieć "otwór liczności" w 32 bitach.

jest bardzo prosty i szybki!

pamiętaj NIE PODWÓJNIE !!! Tylko INT - order jest wartością dokładną!

+0

@AndyM: To nie jest nowe pytanie, to próba odpowiedzi na pytanie – cfi

+0

Oi, źle to odczytałem, moje złe! –

+3

Ale po zaledwie 64 wstawionych wierszach zabraknie wartości dla "zamówienia" (wartość zamówienia będzie mniejsza niż 1) –

1

Obecnie tworzę bazę danych o strukturze drzewa, którą należy zamówić. Używam metody typu lista-linków, która zostanie zamówiona na kliencie (nie w bazie danych). Zamówienie można również wykonać w bazie danych za pomocą kwerendy cyklicznej, ale nie jest to konieczne dla tego projektu.

Zrobiłem ten dokument, który opisuje, w jaki sposób zamierzamy wdrożyć pamięć sortowania, w tym przykład w postgresql. Prosimy o komentarz!

https://docs.google.com/document/d/14WuVyGk6ffYyrTzuypY38aIXZIs8H-HbA81st-syFFI/edit?usp=sharing

+0

+1, dobra próbka. Mam dwa pytania: ** 1st: ** Dlaczego masz zarówno "Parent" i "Preceeding" - Dlaczego podwójnie połączona lista zamiast połączonej listy? ** 2.: ** Jak wybrać uporządkowane rekordy: Wybierz pierwsze 100 rekordów uporządkowanych według ich relacji rodzic/dziecko, na przykład. –

+0

rodzic oznacza węzeł nadrzędny w strukturze drzewa. Poprzednia kolumna określa kolejność węzłów. Niedawno zaktualizowałem przykład w dokumencie za pomocą SELECT, który pobiera węzły w ich prawidłowej kolejności za pomocą rekursywnego wspólnego wyrażenia tabelowego. – Elmer

2

Oto alternatywne podejście z wykorzystaniem wyrażenia tabelowym (CTE).

Podejście to uwzględnia unikalny indeks w kolumnie SortOrder i zamyka wszystkie luki w kolejności sortowania, które mogły pozostać w poprzednich operacjach DELETE.

/* For example, move Product with id = 26 into position 3 */ 
DECLARE @id int = 26 
DECLARE @sortOrder int = 3 


;WITH Sorted AS (
    SELECT Id, 
      ROW_NUMBER() OVER (ORDER BY SortOrder) AS RowNumber 
    FROM Product 
    WHERE Id <> @id 
) 

UPDATE p 
SET  p.SortOrder = 
     (CASE 
      WHEN p.Id = @id THEN @sortOrder 
      WHEN s.RowNumber >= @sortOrder THEN s.RowNumber + 1 
      ELSE s.RowNumber 
     END) 
FROM Product p 
     LEFT JOIN Sorted s ON p.Id = s.Id 
Powiązane problemy