2010-05-13 2 views
5

Ich habe eine Liste in einer Datenbank, die der Benutzer bestellen können sollte.Aktualisieren Sie eine Liste von Dingen, ohne jeden Eintrag zu treffen

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

Auf Last aus der Datenbank, ich order by order_value einfach.

per Drag ‚n Drop, sollte er in der Lage sein apples so zu bewegen, dass sie an der Spitze der Liste angezeigt wird ..

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

Ok. Also jetzt intern muss ich JEDEN LISTE-ARTIKEL aktualisieren! Wenn die Liste 20 oder 100 Elemente enthält, sind das viele Aktualisierungen für einen einfachen Ziehvorgang.

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

Ich würde es lieber mit nur einem Update tun. Eine Möglichkeit, an die ich dachte, ist, ob "interne Bestellung" ein double Wert ist.

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

SO nach dem Drag n‘Drop-Operation, ich zuweisen apples hat einen Wert, der kleiner ist als der Artikel ist es vor erscheinen soll:

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

.. und wenn ein Element irgendwo in die Mitte gezogen, seine order_value ist größer als das man es nach erscheint .. hier zog ich orange zwischen salad und mango zu sein:

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

Irgendwelche Gedanken zu besseren Möglichkeiten, dies zu tun?

+3

Wenn dies vom Menschen generierte Daten sind, dann sind es wahrscheinlich Dutzende von Elementen. Die meisten Menschen würden das Interesse verlieren, indem sie manuell mehr als ein paar Dutzend sortieren würden (und die UI dafür wird über 20 hinausgehen, es sei denn, Sie haben einen echten großen Bildschirm). Mach es einfach. Es wäre eine andere Geschichte, wenn Sie Tausende oder Millionen von Gegenständen zurückgeben würden. – Seth

+0

Müssen Sie gleichzeitige Aktualisierungen zulassen? –

Antwort

0

Ich bin mir nicht sicher, ob dies als Lösung zählt, aber Sie müssen nicht wirklich ein Update für jede Zeile tun. Wenn Sie ‚foo‘ von Position 4 auf Position 1 bewegen, tun Sie gerade

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

Es ist die gleiche Anzahl von Updates, auch wenn Sie von der Position 1000-500 sich bewegen, oder 500-1000 (obwohl Sie‘ ll Notwendigkeit, es zu drehen, natürlich), brauchen Sie nur auf Masse alle betroffenen Zeilen verschieben plus oder minus ein

+0

Ja, ich bin auf der Suche nach kleinen __Worst Case__ Anzahl der Update-Hits – bobobobo

+0

@bob Ich denke, vielleicht haben Sie missverstanden; Das sind zwei Updates, egal wie: Eine, um die tatsächliche Zeile zu ändern, und eine, um alle anderen betroffenen Zeilen zu reparieren. –

0

Sie können wie so es in einem einzigen Update-Anweisung tun:

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 

Sie würden offensichtlich ersetzen apples mit dem ausgewählten Wert. Ich würde jedoch denken, dass diese Art der Sortierung am besten in der Client-Anwendung und nicht in der Datenbank erfolgen würde.

+0

Also, was ist mit einer Einfügung in die Mitte der Liste? Ich nehme an, dass dies in einer gespeicherten Prozedur platziert werden könnte und der Index in der Liste, in der "Äpfel" jetzt "gehen", könnte ein Argument sein. Ich würde dies im Client tun, aber ich muss es sofort in der Datenbank widerspiegeln, falls der Client x aus dem Browser heraus ist, so dass die Ansicht in der Reihenfolge geladen wird, in der der Client sie zuletzt verlassen hat. – bobobobo

+0

@bobobobo - Wie wird Ihr Code "Mitte der Liste" bestimmen, um die Einfügung zu machen? Ich behaupte immer noch, dass dies in der mittleren Schicht wesentlich einfacher wäre als in der Datenbank. Ihr Standortcode könnte die Werte in einem Array speichern und einfach Elemente in dem Array mit der bestimmten Sortierung verschieben. – Thomas

0

Wenn Sie SQL Server verwenden, können Sie dies mithilfe einer verketteten Liste und CTEs tun. Ich weiß nicht, ob mysql CTEs obwohl unterstützt ...

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 

Dies erzeugt die folgenden Ergebnisse vor und nach:

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 unterstützt noch keine CTEs. – Thomas

+0

@Thomas: richtig und es unterstützt nicht die (nicht-standard) [] als Bezeichner Anführungszeichen –

0

Ich nehme an, Sie auf Ihrer Tabelle einen Primärschlüssel haben, eine id Spalte . Diese beiden Aussagen sollten ausreichen.

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; 

Wenn Sie keine ID haben, verwenden Sie stattdessen itemname.

0
  1. Wie bereits vorgeschlagen worden, und es sei denn, Sie an alle Benutzer den aktuellen Auftrag zeigen, dass ein bestimmter Benutzer zu beeinflussen ist, würde ich vorschlagen, dass Sie diese zunächst in den Client zu behandeln (es gibt viele Möglichkeiten, Lösen Sie dies, und dann, basierend auf einer Benutzeraktion (zum Beispiel durch Drücken der Schaltfläche "Ich bin fertig"), aktualisieren Sie die Zeilen in der Datenbank mit der endgültigen Reihenfolge der Struktur, die Sie im Client gespeichert haben.

  2. Sie können den Code im Client so komplex machen, wie Sie möchten, um die Anzahl der Zeilen zu minimieren, die in der Datenbank aktualisiert werden müssen. In einigen Fällen müssen Sie nur eine Zeile einfügen (wenn der Benutzer fügt ein neues Element am Ende der Liste ein); In vielen Fällen müssen Sie möglicherweise zwei Zeilen aktualisieren (wenn der Benutzer nur zwei aufeinanderfolgende Elemente tauscht). Das schlimmste Szenario gemäß der Anzahl der Zeilen, die aktualisiert werden müssen, sind alle Zeilen (Sie können einen Algorithmus einrichten, der nur die Zeilen erkennt, die aktualisiert werden müssen, und diese nur aktualisieren). Sie haben die Wahl, ob es sich lohnt oder nur ein Update aller Zeilen.

  3. Die Quintessenz ist, dass Sie nicht alle Zeilen in der Datenbank aktualisieren müssen, diese Situation ist nur eines der vielen möglichen Szenarien. Einige Datenbanken erlauben eine Aktualisierung in großen Mengen (der Name kann von einer Datenbank zur anderen variieren) und dies wird nicht sehr teuer sein.

2

Angenommen @old ist der Wert 4 für die alte Position von Äpfeln und @new ist die neue Position 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); 

I verwendet 5.1.52 MySQL diese Daten auf Ihrem Beispiel zu testen und funktioniert. Das identische SQL funktioniert auch, wenn Sie einen frühen Eintrag verschieben müssen, um später zu sein, oder einen in der Mitte verschieben usw. Setzen Sie einfach die Werte @old und @new.

0

Wenn die Spalte nur die Reihenfolge der Zeilen angibt, sehe ich nichts falsch bei der Verwendung von 0 und Negativen.

Verwandte Themen