在mysql中重新排序列数据

时间:2011-12-16 09:28:07

标签: mysql

我有一张这样的表:

categoryID      categoryName
----------------------------
     1            A
     2            B
     3            C

现在我希望用户能够根据他的意愿订购这些数据。我想记住他对未来的首选订单。所以我想我会在上面的表格中添加一列order,并将其设为INTAUTO_INCREMENT类型。所以现在我得到一张这样的表:

categoryID      categoryName    order
-------------------------------------
     1            A               1
     2            B               2
     3            C               3
     4            D               4

我的问题是 - 用户现在决定,将categoryName与订单4(上例中的D)一起带到2(上例中的B以上),这样表格现在看起来像:

categoryID      categoryName    order
-------------------------------------
     1            A               1
     2            B               3
     3            C               4
     4            D               2

我的问题是 - 当重新排序发生时,我应该如何为order列分配新值。有没有办法在不更新表中的所有行的情况下执行此操作?

我想到的一种方法是将列设为FLOAT,如果我想在列之间输入1,2,则将其命令为1.5。在这种情况下,当我重新订购商品时,我一直在失去精确度。

编辑: 另一种是更新(m,n)之间的所有行,其中m,n分别是源和目标订单。但这意味着运行(m-n)单独的查询不是吗?

编辑2: 假设我采用FLOAT方法,我想出了这个sql来计算需要在id = 2的项目之后插入的项目的订单值(例如)。

select ((
    select `order` as nextHighestOrder
    from `categories`
    where `order` > (
        select `order` as targetOrder 
        from `categories` 
        where `categoryID`=2) 
        limit 1) + (
            select `order` as targetOrder 
            from `categories` 
            where `categoryID`=2)) / 2;

这给了我3.5这就是我想要实现的目标。

有没有更好的方法来写这个?请注意select order as targetOrder from categories where categoryID=9执行两次。

4 个答案:

答案 0 :(得分:4)

如果更改的数量相当小,如果您知道所涉及项目的ID,则可以生成一个笨拙但相当有效的UPDATE语句:

UPDATE categories
JOIN (
    SELECT 2 as categoryID, 3 as new_order
    UNION ALL
    SELECT 3 as categoryID, 4 as new_order
    UNION ALL
    SELECT 4 as categoryID, 2 as new_order) orders
USING (categoryId)
SET `order` = new_order;

或(我更喜欢):

UPDATE categories
SET `order` = ELT (FIND_IN_SET (categoryID, '2,3,4'),
                   3, 4, 2)
WHERE categoryID in (2,3,4);

<强> UPD

假设您知道类别(或其名称)的当前ID,旧位置和新位置,您可以使用以下查询在列表中移动类别(为了向上移动,您必须更改between条件和new_rank计算到rank+1):

SET @id:=2, @cur_rank:=2, @new_rank:=4;

UPDATE t1
JOIN (
  SELECT categoryID, (rank - 1) as new_rank
  FROM t1
  WHERE rank between @cur_rank + 1 AND @new_rank
  UNION ALL
  SELECT @id as categoryID, @new_rank as new_rank
) as r
USING (categoryID)
SET rank = new_rank;

答案 1 :(得分:0)

Float的想法听起来很难理解,只是不向用户显示这些数字 - )

每当用户向上或向下移动条目时,您都可以找出上方和下方的条目。只需获取他们的订单号并找到平均值 - 这是已移动的条目的新订单。

答案 2 :(得分:0)

您可以将顺序保持为整数,并对拖动源索引和目标索引之间的所有项重新编号,因为它们无法拖动那么远,尤其是只有20个奇数类别。 Mulit-item drags使这更加复杂。

浮动更容易,但每次移动你发现中间你可以很快耗尽精确度,我会写一个测试,检查它不会最终放弃工作如果你继续移动第3项第二个位置一遍又一遍。

示例:

1,2,3
Move 3rd to 2nd
1,1.5,2
Move 3rd to 2nd
1,1.25,1.5
Move 3rd to 2nd
1,1.125,1.25

在excel电子表格中执行此操作,您会发现该数字对于浮点数来说太小了,无法在大约30次迭代中处理。

答案 3 :(得分:0)

好的,这与@newtover建议的相同,但是这两个简单的查询可以被任何其他开发人员更容易理解,即使是没有经验的人。

假设我们有一个表t1:

id      name    position
-------------------------------------
 1      A       1
 2      B       2
 3      C       3
 4      D       4
 5     -E-      5
 6      F       6

让我们将id = 5的项目'E'移动到第二个位置:

1)增加项目'E'的旧位置和'E'的所需位置(位置2,3,4)之间所有项目的位置

UPDATE t1 SET position=position+1 WHERE position BETWEEN 2 AND 4

2)现在位置2没有项目,所以'E'可以占据它的位置

UPDATE t1 SET position=2 WHERE id=5

结果,按'位置'排序

id      name    position
-------------------------------------
 1      A       1
 5     -E-      2
 2      B       3
 3      C       4
 4      D       5
 6      F       6

只有2个简单的查询,没有子查询。

限制:列“位置”不能是唯一的。但也许有一些修改也应该有效。

尚未对大型数据集进行测试。