我在数据库中有一个用户应该可以订购的列表。
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
确定。所以现在在内部我必须更新每个列表项目!如果列表有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
和{之间{1}}:
mango
有关更好的方法的任何想法?
答案 0 :(得分:2)
假设@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 :(得分:1)
我最终使用adjacencies table。我当时不知道。
答案 2 :(得分:0)
我不确定这是否算作解决方案,但你并不需要为每一行做一次更新。如果你将'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(虽然你需要自然地翻转它),它的更新次数相同,你只需要移动所有受影响的行加上减去一个
答案 3 :(得分:0)
您可以在单个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
。但是,我认为这种类型的排序最好在客户端应用程序中完成,而不是在数据库中完成。
答案 4 :(得分:0)
如果您使用的是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
答案 5 :(得分:0)
我猜你的桌子上有一个主键,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;
如果您没有ID,请改用itemname。
答案 6 :(得分:0)
如前所述,除非您必须向所有用户显示给定用户正在影响的当前订单,否则我建议您先在客户端处理此问题(有很多解决方法)然后,基于用户操作(例如,按下“我完成”按钮),您将使用您选择存储在客户端中的结构的最终顺序更新数据库中的行。
您可以使客户端中的代码尽可能复杂,以尽量减少需要在数据库中更新的行数:在某些情况下,您可能只需要插入一行(如果用户在列表的末尾插入一个新项目;);在许多情况下,您可能需要更新两行(如果用户只是交换两个连续的项目)。根据需要更新的行数,最糟糕的情况是所有行(您可以设置一个只检测需要更新的行并只更新那些行的算法)。您可以选择是执行此操作还是仅发布所有行的更新。
最重要的是,您不需要更新数据库中的所有行,这种情况只是众多可能情况中的一种。有些数据库允许批量更新(名称可能因数据库而异),这不会非常昂贵。
答案 7 :(得分:0)
如果列只是指定了行的顺序,我认为使用0和否定没有任何问题。