Обновите список вещей, не нажимая каждую запись - PullRequest
6 голосов
/ 13 мая 2010

У меня есть список в базе данных, который пользователь должен иметь возможность заказать.

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

При загрузке из базы данных я просто order by order_value.

С помощью перетаскивания он должен переместиться на apples так, чтобы оно появилось вверху списка.

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

Ok. Так что теперь внутри я должен обновить каждый пункт списка! Если в списке 20 или 100 элементов, это много обновлений для простой операции перетаскивания.

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

Я бы лучше сделал это только с одним обновлением. Один из способов, о котором я подумал, - это если «внутренний порядок» равен double.

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

ТАК после операции перетаскивания я назначаю apples значение, которое меньше, чем элемент, перед которым он должен появиться:

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

.. и если предмет куда-то перетаскивается в середину, его order_value больше, чем тот, после которого он появляется .. здесь я переместил orange, чтобы быть между salad и mango:

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

Есть мысли о лучших способах сделать это?

Ответы [ 8 ]

2 голосов
/ 14 февраля 2011

Предположим, @old - это значение 4 для старой позиции яблок, а @new - это новая позиция 1.

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

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

Я использовал MySQL 5.1.52, чтобы проверить это на данных вашего примераи это работает.Идентичный SQL также работает, если вам нужно переместить более раннюю запись на более позднюю, или переместить одну в середину и т. Д. Просто установите значения @old и @new.

1 голос
/ 14 февраля 2011

Я использовал таблицу смежностей . Я не знал об этом в то время.

0 голосов
/ 14 февраля 2011

Если в столбце просто указан порядок строк, я не вижу ничего плохого в использовании 0 и негативов.

0 голосов
/ 14 мая 2010
  1. Как было предложено ранее, и если вам не нужно показывать всем пользователям текущий порядок, на который воздействует данный пользователь, я бы посоветовал сначала обработать это на клиенте (их многоспособы решения этой проблемы), а затем, основываясь на действиях пользователя (например, нажав кнопку «Я готов»), вы обновляете строки в базе данных в соответствии с окончательным порядком из структуры, которую вы выбрали для хранения в клиенте..

  2. Вы можете сделать код на клиенте настолько сложным, насколько хотите, чтобы минимизировать количество строк, которые необходимо обновить в базе данных: в некоторых случаях вы можете тольконеобходимо вставить одну строку (если пользователь вставляет новый элемент в конец списка);во многих случаях вам может потребоваться обновить две строки (если пользователь просто поменяет два последовательных элемента).Наихудший сценарий по количеству строк, которые необходимо обновить, - это все строки (вы можете использовать алгоритм, который будет обнаруживать только строки, которые нужно обновить, и просто обновлять их).Выбор за вами, стоит ли это делать или просто обновить все строки.

  3. Суть в том, что вам не нужно обновлять все строки в базе данных,эта ситуация является лишь одним из многих возможных сценариев.Некоторые базы данных допускают массовое обновление (имя может варьироваться от одной базы данных к другой), и это не будет очень дорогим.

0 голосов
/ 13 мая 2010

Я полагаю, у вас есть первичный ключ в вашей таблице, столбец id. Эти два утверждения должны сделать.

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;

Если у вас нет идентификатора, используйте вместо него имя элемента.

0 голосов
/ 13 мая 2010

Если вы используете SQL Server, вы можете сделать это, используя представление связанного списка и CTE. Я не знаю, поддерживает ли mysql CTE, хотя ...

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

Это дает следующие результаты до и после:

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
0 голосов
/ 13 мая 2010

Вы можете сделать это в одном выражении Update, например:

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

Вы, очевидно, замените apples выбранным значением. Однако я думаю, что этот тип сортировки лучше всего выполнять в клиентском приложении, а не в базе данных.

0 голосов
/ 13 мая 2010

Я не уверен, считается ли это решением, но вам буквально не нужно делать одно обновление для каждой строки. Если вы переместите 'foo' из положения 4 в положение 1, вы просто выполните

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

Это такое же количество обновлений, даже если вы перемещаетесь с позиции 1000 на 500 или с 500 на 1000 (хотя вам, конечно, нужно будет перевернуть ее), вам просто нужно массово сдвинуть все затронутые строки плюс или минус один

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...