2010-05-13 12 views
5

Mam listę w bazie danych, którą użytkownik powinien móc zamówić.Zaktualizuj listę rzeczy bez uderzania w każdy wpis

itemname| order value (int) 
--------+---------------------   
salad | 1 
mango | 2 
orange | 3 
apples | 4 

Po załadowaniu z bazy danych po prostu order by order_value.

poprzez przeciągnięcie „n upuść, powinien on być w stanie poruszać apples tak, że pojawia się na szczycie listy ..

itemname| order value (int) 
--------+---------------------   
apples | 4 
salad | 1 
mango | 2 
orange | 3 

Ok. Teraz wewnętrznie muszę aktualizować KAŻDĄ LISTĘ! Jeśli lista zawiera 20 lub 100 elementów, oznacza to wiele aktualizacji dla prostej operacji przeciągania.

itemname| order value (int) 
--------+---------------------   
apples | 1 
salad | 2 
mango | 3 
orange | 4 

Wolę zrobić to z tylko jedną aktualizacją. Jednym ze sposobów, o których myślałem było to, że "wewnętrzny porządek" jest wartością double.

itemname| order value (double) 
--------+---------------------   
salad | 1.0 
mango | 2.0 
orange | 3.0 
apples | 4.0 

więc po przeciągania n”operacji upuszczenia przypisać apples ma wartość, która jest mniejsza niż elementu, którym ma zostać umieszczona z przodu:

itemname| order value (double) 
--------+---------------------   
apples | 0.5 
salad | 1.0 
mango | 2.0 
orange | 3.0 

.. i, jeśli element jest wciągnięty w środku gdzieś, jego order_value jest większy niż ten, który pojawia się po .. tutaj przeniosłem orange się między salad i mango:

itemname| order value (double) 
--------+---------------------   
apples | 0.5 
salad | 1.0 
orange | 1.5 
mango | 2.0 

Jakieś przemyślenia na temat lepszych sposobów na zrobienie tego?

+3

Jeśli jest to dane generowane przez człowieka, to w maksimum jest to prawdopodobnie dziesiątki elementów. Większość ludzi straciłaby zainteresowanie ręcznie, sortując więcej niż kilkadziesiąt (a interfejs do tego będzie się łamał po około 20, chyba że masz naprawdę duży ekran). Po prostu to zrób. Inaczej by było, gdybyś uciekał się do tysięcy lub milionów przedmiotów. – Seth

+0

Czy musisz zezwolić na równoczesne aktualizacje? –

Odpowiedz

0

Nie jestem pewien, czy to się liczy jako rozwiązanie, ale nie musisz wykonywać jednej aktualizacji dla każdego wiersza. Jeśli przesuniesz „foo” z pozycji 4 do pozycji 1, po prostu zrobić

UPDATE table SET position = 1 WHERE itemname = 'foo' 
UPDATE table SET position = position + 1 WHERE itemname != 'foo' AND position < 4 

To ta sama liczba aktualizacji, nawet jeśli jesteś w ruchu od pozycji 1000 do 500, lub od 500 do 1000 (chociaż” ll trzeba go odwrócić, naturalnie), wystarczy do masy przesunięcie wszystkich wierszy plus lub minus jeden

+0

Tak, szukam małego przypadku __worst__ liczba aktualizacji trafień – bobobobo

+0

@bob Myślę, że być może źle zrozumiałeś; to są dwie aktualizacje bez względu na to: jedna do zmiany rzeczywistego wiersza i jedna do poprawienia wszystkich innych dotkniętych wierszy. –

0

można to zrobić w jednej instrukcji UPDATE tak:

Update Table 
Set OrderValue = Case 
        When Table.ItemName = 'apples' Then 0 
        Else (
          Select Count(*) 
          From Table As T1 
          Where T1.ItemName <> 'apples' 
           And T1.OrderValue < Table.OrderValue 
          ) + 1 
        End + 1 

można by oczywiście zastąpić apples z wybraną wartością. Sądzę jednak, że tego rodzaju sortowanie najlepiej byłoby wykonać w aplikacji klienckiej, a nie w bazie danych.

+0

Co z umieszczeniem na środku listy? Przypuszczam, że może to być umieszczone w procedurze przechowywanej, a indeks na liście, gdzie "jabłka" teraz "idzie" może być argumentem. Zrobiłbym to w kliencie, ale muszę natychmiast umieścić go w bazie danych na wypadek, gdyby klient x wyszedł z przeglądarki, tak aby widok był ładowany w kolejności, w jakiej klient go zostawił. – bobobobo

+0

@bobobobo - W jaki sposób Twój kod określa "środek listy", aby wstawić? Nadal twierdzę, że byłoby to znacznie łatwiejsze w warstwie pośredniej niż w bazie danych. Twój kod witryny może przechowywać wartości w tablicy i po prostu przenosić elementy w tablicy z danym rodzajem. – Thomas

0

Jeśli korzystasz z SQL Server, możesz to zrobić, korzystając z listy połączonych list i CTE. Nie wiem, czy mysql obsługuje współczynniki CTE chociaż ...

SET NOCOUNT ON 
GO 

DROP TABLE [Item] 
GO 

CREATE TABLE [Item] 
(
    [ItemId] int NOT NULL PRIMARY KEY, 
    [Name] varchar(100) NOT NULL, 
    [PreviousId] int NULL 
) 
GO 

INSERT [Item] VALUES (6, 'apples', 3) 
INSERT [Item] VALUES (3, 'orange', 36) 
INSERT [Item] VALUES (9, 'mango', 100) 
INSERT [Item] VALUES (100, 'salad', NULL) 
INSERT [Item] VALUES (36, 'banana', 9) 
GO 

;WITH 
[LinkedItem] AS 
(
    SELECT 
     [Item].*, 
     1 AS [OrderValue] 
    FROM [Item] 
    WHERE [Item].[PreviousId] IS NULL 
    UNION ALL 
    SELECT 
     [Item].*, 
     [LinkedItem].[OrderValue] + 1 
    FROM [Item] 
     INNER JOIN [LinkedItem] ON [LinkedItem].[ItemId] = [Item].[PreviousId] 
) 
SELECT * 
FROM [LinkedItem] 
ORDER BY 
    [LinkedItem].[OrderValue] 

-- Drag orange up two spaces 
DECLARE @MovingItemId int 
DECLARE @NewPreviousId int 
SET @MovingItemId = 3 
SET @NewPreviousId = 100 

DECLARE @OldPreviousId int 
SELECT @OldPreviousId = [PreviousId] FROM [Item] WHERE [ItemId] = @MovingItemId 
UPDATE [Item] SET [PreviousId] = @OldPreviousId WHERE [PreviousId] = @MovingItemId 
UPDATE [Item] SET [PreviousId] = @MovingItemId WHERE [PreviousId] = @NewPreviousId 
UPDATE [Item] SET [PreviousId] = @NewPreviousId WHERE [ItemId] = @MovingItemId 

To daje następującą przed i po wyniki:

100 salad NULL 1 
9 mango 100 2 
36 banana 9 3 
3 orange 36 4 
6 apples 3 5 

100 salad NULL 1 
3 orange 100 2 
9 mango 3 3 
36 banana 9 4 
6 apples 36 5 
+1

AFAIK, MySQL jeszcze nie obsługuje CTE. – Thomas

+0

@Thomas: poprawny i nie obsługuje (niestandardowego) [] jako cytatu z identyfikatorem –

0

Przypuszczam masz klucza podstawowego w tabeli, kolumny id . Te dwa stwierdzenia powinny wystarczyć.

update table set order_value=0 where itemname='apples'; 
update 
(select @num := 0)vars 
straight_join 
(select id, @num := @num+1 as ord_value 
from table 
order by order_value 
)big 
inner join table t on t.id = big.id 
set t.order_value = big.ord_value; 

Jeśli nie masz identyfikatora, użyj zamiast niego nazwy przedmiotu.

0
  1. Jak sugerowano wcześniej, a jeśli nie masz, aby pokazać wszystkich użytkowników aktualnego porządku, że dany użytkownik dotyka, chciałbym zaproponować, że traktujesz to w pierwszym klientem (istnieje wiele sposobów rozwiązując to), a następnie, na podstawie akcji użytkownika (np. naciskając przycisk "Gotowe") aktualizujesz wiersze w bazie danych o ostateczne zamówienie ze struktury, którą wybrałeś do przechowywania w kliencie.

  2. Możesz uczynić kod w kliencie tak złożonym, jak chcesz spróbować zminimalizować liczbę wierszy, które wymagają aktualizacji w bazie danych: w niektórych przypadkach może być konieczne tylko wstawienie jednego wiersza (jeśli użytkownik wstawia nowy element na końcu listy); w wielu przypadkach konieczne może być zaktualizowanie dwóch wierszy (jeśli użytkownik po prostu zamienia dwa kolejne elementy). Najgorszym scenariuszem, jeśli chodzi o liczbę wierszy, które należy zaktualizować, są wszystkie wiersze (można przydzielić algorytm, który wykrywa wiersze, które wymagają aktualizacji i po prostu aktualizuje je). Wybór należy do Ciebie, czy warto to zrobić, czy po prostu wydać aktualizację wszystkich wierszy.

  3. Najważniejsze jest to, że nie trzeba aktualizować wszystkich wierszy w bazie danych, że jest to tylko jeden z wielu możliwych scenariuszy. Niektóre bazy danych umożliwiają zbiorczą aktualizację (nazwa może się różnić w zależności od bazy danych) i nie będzie to zbyt kosztowne.

2

Załóżmy @old jest wartość 4 dla starej pozycji jabłek i @new to nowa pozycja 1.

set @old = 4; 
set @new = 1; 

UPDATE Items 
SET `order value` = 
    CASE `order value` WHEN @old THEN @new 
    ELSE `order value` + SIGN(@[email protected]) END 
WHERE `order value` BETWEEN LEAST(@old, @new) AND GREATEST(@old, @new); 

użyłem MySQL 1.5.52 przetestować to na swoim przykładzie danych i to Prace. Identyczny SQL działa również, jeśli musisz przesunąć wcześniejszy wpis na później lub przenieść go na środku itp. Ustaw wartości @old i @new.

0

Jeśli kolumna po prostu określa kolejność rzędów, nie widzę niczego złego w używaniu 0 i negatywów.

Powiązane problemy