重新排序有序列表

时间:2010-07-31 18:53:49

标签: sql database-design

我有一个带数据的以下SQL表

ProductList 
id  order productname
79   1     name1
42   2     name2
67   3     somename
88   4     othername
99   5     XYZ
66   6     ABC

显示顺序非常不稳定,它会经常更改,用户会添加或删除项目并重新排序项目。

如何在不更新多条记录的情况下处理这种情况。示例:如果用户输入1到2个订单之间的新产品,我不想更新2以下的所有记录的顺序,如果有人切换订单3到4,我不想更新3以下的每个记录。

8 个答案:

答案 0 :(得分:7)

使用由旧BASIC编码器制作的“可怕的旧技巧” - 将您的订单设置为100,200,300,400等,然后您可以在需要时选择“中间”的订单。这可能会变得混乱 - 如果您预计会进行大量的重新排序,那么我建议您有一个计划任务来对整个表格中的订单值进行“重新排序”。

答案 1 :(得分:4)

您有两种选择:

  • 使用带有函数/ etc的UPDATE语句迭代行,以生成更新的订单值
  • Scorched Earth:您删除现有记录,并插入相同的记录,保存更正的订单值

没有SQL功能可以让这更容易,而且没有简单的真正选项。

答案 2 :(得分:4)

同一问题的第二个答案:

使用DOUBLE字段进行排序并拆分要插入的两个值之间的差异。在任何标准的商业应用程序中,我非常非常怀疑你是否会接近无法解决排序顺序差异的插入数量。

答案 3 :(得分:3)

您可以使用字符串代替无数个数字。如果要在“1”和“2”之间插入,请将其设为“15”。

答案 4 :(得分:1)

您可以使用链接列表进行排序(使用某种特殊方式识别头部)。

插入是一个INSERT和一个UPDATE。删除是一个DELETE和一个UPDATE。此举是三次更新。 (当然,使用事务来确保链表不会中断。)

答案 5 :(得分:1)

添加名为OrderDateTime的DATETIME列。使用此列(按降序排列)解决排序中的“关系”,并仅在进行排序操作时更新它。

例如,在您的示例中,假设所有行都具有昨天的OrderDateTime值。

现在,要在1和2之间插入一个项目,你可以将它的Order值设置为2,并将它的OrderDateTime值设置为now。当SELECT * FROM ProductList ORDER BY Order ASC,OrderDateTime DESC时,新的2号项目将在现有项目之前排序。

同样,要交换第4项和第5项,您需要更新第5项,订单为4,OrderDateTime为现在。它将成为一个更近期的4项并且更早出现。

如果您尝试在已经具有相同订单值的其他两个项目之间插入项目,则需要注意分割OrderDateTime值差异。

答案 6 :(得分:0)

在我的情况下,我允许用户重新排序他们的巡演中的步骤。我有一个名为STEP的列,他们可以更改,我创建了一个名为STEP2的列。 STEP2列自动更新为与步骤相同的值。但是当用户更改STEP时,我按照建议的更改和原始顺序(STEP2)DESC进行排序。用户将STEP更新为新值后,我保存到数据库。然后我运行一个proc来重新排序步骤。

proc是:

DECLARE @t StackTable

INSERT INTO @t(Id)
SELECT Id
FROM TourStops
WHERE TourIdRef = @tourId
ORDER BY [Step], [Step2] DESC

UPDATE TourStops
SET [Step] = t.Position, [Step2] = t.Position
FROM TourStops s join @t t on s.Id = t.Id

StackTable是一个包含2列的UDT:Id和Position。 Position是IDENTITY列。每次创建时,它都以1开头,每增加一行,增量为1。

答案 7 :(得分:0)

根据Larry Lustig关于使用双人的建议,我想出了这个。它运行在一个10K行的表中,运行速度非常快。当然欢迎提高效率的建议!

--- Note:
--- [Order] column is a value type of double
--- when inserting into MyTable, set [Order] to
--- MAX([Order])+10
CREATE PROCEDURE dbo.MoveUpLevel
(
    @MyTableID int
)
AS
BEGIN
SET NOCOUNT ON


DECLARE @LevelMe float
DECLARE @LevelMin float
DECLARE @LevelMax float
SELECT @LevelMe = [Order]
    FROM dbo.MyTable WITH (NOLOCK)
    WHERE MyTableID = @MyTableID
SELECT @LevelMin = MIN([Order])
      ,@LevelMax = MAX([Order])
    FROM dbo.MyTable WITH (NOLOCK)

--- Check if already at the top
IF @LevelMe = @LevelMin
    RETURN(0)

DECLARE @LevelAboveMe float
SELECT TOP 1 @LevelAboveMe = [Order]
    FROM dbo.MyTable WITH (NOLOCK)
    WHERE [Order] < @LevelMe
    ORDER BY [Order] DESC

DECLARE @LevelAboveMe2 float = 0
IF NOT @LevelAboveMe = @LevelMin
    SELECT TOP 1 @LevelAboveMe2 = [Order]
        FROM dbo.MyTable WITH (NOLOCK)
        WHERE [Order] < @LevelAboveMe
        ORDER BY [Order] DESC

-- calculate new level
SET @LevelMe = @LevelAboveMe2 + ((@LevelAboveMe - @LevelAboveMe2)/2)
-- store to DB
UPDATE dbo.MyTable
    SET [Order] = @LevelMe
    WHERE MyTableID = @MyTableID        

RETURN(0)
END
GO

CREATE PROCEDURE dbo.MoveDownLevel
(
     @MyTableID int
)
AS
BEGIN
SET NOCOUNT ON

DECLARE @LevelMe float
DECLARE @LevelMin float
DECLARE @LevelMax float
SELECT @LevelMe = [Order]
    FROM dbo.MyTable WITH (NOLOCK)
    WHERE MyTableID = @MyTableID
SELECT @LevelMin = MIN([Order])
      ,@LevelMax = MAX([Order])
    FROM dbo.MyTable WITH (NOLOCK)

--- Check if already at the bottom
IF @LevelMe = @LevelMax
    RETURN(0)

DECLARE @LevelBelowMe float
SELECT TOP 1 @LevelBelowMe = [Order]
    FROM dbo.MyTable WITH (NOLOCK)
    WHERE [Order] > @LevelMe
    ORDER BY [Order] ASC

DECLARE @LevelBelowMe2 float = @LevelMax + 10
IF NOT @LevelBelowMe = @LevelMax
    SELECT TOP 1 @LevelBelowMe2 = [Order]
        FROM dbo.MyTable WITH (NOLOCK)
        WHERE [Order] > @LevelBelowMe
        ORDER BY [Order] ASC

-- calculate new level
SET @LevelMe = @LevelBelowMe + ((@LevelBelowMe2 - @LevelBelowMe)/2)

-- store to DB
UPDATE dbo.MyTable
    SET [Order] = @LevelMe
    WHERE MyTableID = @MyTableID        

RETURN(0)
END
GO