表中的SQL Server自定义记录排序,允许删除记录

时间:2012-07-03 09:29:08

标签: sql sql-server tsql sorting

带有自定义排序的SQL Server表包含以下列: ID (PK,自动增量), OrderNumber Col1 Col2 ..

默认情况下,插入触发器会根据建议here将值从ID复制到OrderNumber。 使用一些可视化界面,用户可以通过递增或递减OrderNumber值来对记录进行排序。

但是,如何在此期间处理被删除的记录?

示例: 假设您添加PK ID的记录:1,2,3,4,5 - OrderNumber接收相同的值。然后删除ID = 4,ID = 5的记录。下一条记录的ID = 6,OrderNumber将获得相同的值。 拥有2个缺少OrderNumbers的范围会强制用户减少ID = 6的记录,如3次更改它的顺序(即按下3x按钮)。

或者,可以将select count(*) from table插入OrderNumber,但是当删除一些旧行时,它允许在表中有几个类似的值。

如果一个人不删除记录,但只是“停用”它们,它们仍然按排序顺序包含在内,对用户来说是不可见的。目前,需要Java解决方案,但我认为这个问题与语言无关。

这方面有更好的方法吗?

3 个答案:

答案 0 :(得分:1)

我只是修改了切换OrderNumber值的脚本,以便正确地执行,而依赖于它们没有间隙。

我不知道你的脚本接受什么参数以及它如何使用它们,但是我最终想出的那个参数接受要移动的项目的ID和要移动的位置的数量(负值)意味着“朝着较低的OrderNumber值”,而积极的意味着相反的方向。)

这个想法如下:

  1. 查找指定项目的OrderNumber

  2. 按照第二个参数确定的方向对从OrderNumber开始的所有项目进行排名。因此,指定的项目将获得1

  3. 的排名
  4. 选择排名从1到第二个参数绝对值加1的项目。 (即,最后一项是指定项目移动到的项目。)

  5. 将结果集合与自身连接,以便每一行与下一行连接,最后一行与第一行连接,从而使用一组行来更新另一行。

  6. 这是实现上述内容的查询,其中的注释解释了一些棘手的部分:

    已编辑:修正了错误重新排序的问题

    /* these are the arguments of the query */
    DECLARE @ID int, @JumpBy int;
    SET @ID = ...
    SET @JumpBy = ...
    
    DECLARE @OrderNumber int;
    /* Step #1: Get OrderNumber of the specified item */
    SELECT @OrderNumber = OrderNumber FROM atable WHERE ID = @ID;
    
    WITH ranked AS (
      /* Step #2: rank rows including the specified item and those that are sorted
         either before or after it (depending on the value of @JumpBy */
      SELECT
        *,
        rnk = ROW_NUMBER() OVER (
          ORDER BY OrderNumber * SIGN(@JumpBy)
          /* this little "* SIGN(@JumpBy)" trick ensures that the
             top-ranked item will always be the one specified by @ID:
             * if we are selecting rows where OrderNumber >= @OrderNumber,
               the order will be by OrderNumber and @OrderNumber will be
               the smallest item (thus #1);
             * if we are selecting rows where OrderNumber <= @OrderNumber,
               the order becomes by -OrderNumber and @OrderNumber again
               becomes the top ranked item, because its negative counterpart,
               -@OrderNumber, will again be the smallest one
          */
        )
      FROM atable
      WHERE OrderNumber >= @OrderNumber AND @JumpBy > 0
         OR OrderNumber <= @OrderNumber AND @JumpBy < 0
    ),
    affected AS (
      /* Step #3: select only rows that need be affected */
      SELECT *
      FROM ranked
      WHERE rnk BETWEEN 1 AND ABS(@JumpBy) + 1
    )
    /* Step #4: self-join and update */
    UPDATE old
    SET OrderNumber = new.OrderNumber
    FROM affected old
      INNER JOIN affected new ON old.rnk = new.rnk % (ABS(@JumpBy) + 1) + 1
                /* if old.rnk = 1, the corresponding new.rnk is N,
                   because 1 = N MOD N + 1  (N is ABS(@JumpBy)+1),
                   for old.rnk = 2 the matching new.rnk is 1: 2 = 1 MOD N + 1,
                   for 3, it's 2 etc.
                   this condition could alternatively be written like this:
                   new.rnk = (old.rnk + ABS(@JumpBy) - 1) % (ABS(@JumpBy) + 1) + 1
                 */
    

    注意:这假定为SQL Server 2005或更高版本。

    此解决方案的一个已知问题是,如果指定的ID无法完全按指定的位置移动(例如,如果要将最上面的行向上移动任意数量的位置),则不会正确“移动”行职位,或第二行由两个或更多职位等。)。

答案 1 :(得分:0)

好的 - 如果我没弄错的话,你想要对OrderNumber进行碎片整理。 如果您使用ROW_NUMBER(),该怎么办?

示例:

;WITH calc_cte AS (
  SELECT
    ID
    , OrderNumber
    , RowNo = ROW_NUMBER() OVER (ORDER BY ID)
  FROM
    dbo.Order    
)
UPDATE
  c
SET
  OrderNumber = c.RowNo
FROM
  calc_cte c
WHERE EXISTS (SELECT * FROM inserted i WHERE c.ID = i.ID)

答案 2 :(得分:0)

不想回答我自己的问题,但我相信我找到了解决方案。

插入查询

INSERT INTO table (OrderNumber, col1, col2) 
VALUES ((select count(*)+1 from table),val1,val2)

删除触发器:

CREATE TRIGGER Cleanup_After_Delete ON table
AFTER DELETE AS
BEGIN
  WITH rowtable AS (SELECT [ID], OrderNumber, rownum = ROW_NUMBER() 
                    OVER (ORDER BY OrderNumber ASC) FROM table)
  UPDATE rt SET OrderNumber = rt.rownum FROM rowtable rt 
  WHERE OrderNumber >= (SELECT OrderNumber FROM deleted)
END

每次删除后触发器都会触发,并更正已删除的所有OrderNumbers(无间隙)。这意味着我可以通过切换OrderNumbers来简单地改变2条记录的顺序。


对于我的问题,这是一个有效的解决方案,但this one也非常好,可能对其他人更有用。