如何在SQL Server中创建类似于结构的队列

时间:2018-12-10 22:08:06

标签: sql sql-server tsql queue

是否有很好的方法在SQL Server中创建类似于队列的结构?

要求:

  1. 插入行时,我希望它们默认为队列底部
  2. 选择行时,我希望能够轻松地排在队列的顶部
  3. 这是艰难的一步:我希望能够轻松地将某些内容移到队列中,并重新调整其余部分的方向。例如:将项目5移至数字1,然后1-4变为2-5

一个简单的标识列可以满足需求1和2,但是我将如何处理3?

解决方案

我最终从@ roger-wolf实施了解决方案 一个区别是,我使用触发器而不是存储过程来重新编号。这是我的触发代码:

CREATE TRIGGER [dbo].[TR_Queue]
    ON [dbo].[Queue]
    AFTER INSERT, DELETE, UPDATE
AS 
BEGIN
    SET NOCOUNT ON;

    -- Get the current max value in priority
    DECLARE @maxPriority INT = COALESCE((SELECT MAX([priority]) FROM [dbo].[Queue]), 0);

    WITH newValues AS (
        -- Renumber by priority, starting at 1
        SELECT [queueID]
            ,ROW_NUMBER() OVER(ORDER BY [priority] ASC) AS [priority]
        FROM (
            -- Pretend all nulls are greater than previous max priority
            SELECT [queueID]
                ,COALESCE([priority], @maxPriority+1) AS [priority]
            FROM [dbo].[Queue]
        ) AS tbl
    )
    UPDATE q
    SET q.[priority] = newValues.[priority]
    FROM [dbo].[Queue] AS qroger-wolf
    INNER JOIN newValues
        ON q.[queueID] = newValues.[queueID]
END

这对我来说效果很好,因为队列总是相对较小并且不经常更新,因此我不必处理触发器的性能。

3 个答案:

答案 0 :(得分:3)

使用float列进行优先级划分,并采用类似于Celko树的方法:

  • 如果您有优先级为1、2和3的项目,而最后一个需要排在第二位,则计算其新邻居之间的平均值,在此示例中为1.5;
  • 如果另一个需要排第二,则其优先级将为1.25。这可以持续一段时间;
  • 按优先级显示排队的项目时,请使用row_number()代替UI中的浮点值;
  • 如果项目之间的距离太近(例如1e-10或更小),请准备好存储过程以将它们重新编号为整数。

我在这里看到的唯一缺陷是,当队列中的第N个项目既不是第一个也不是最后一个时,要找到第N个项目变得更加困难。如果不需要的话,该方法应该可行。

答案 1 :(得分:2)

您可以添加类型Priority的{​​{1}}列,并且当您将某行设置为优先级行时,可以在DateTime列中设置当前日期时间,然后使用该日期时间是您的Priority标准的一部分?

答案 2 :(得分:0)

我在过去的项目中有类似的要求,我做了什么(并且奏效了):

  1. 添加类型为update_at_utc的列datetime2
  2. 插入时,设置update_at_utc = GETDATEUTC()
  3. 检索时,按update_at_utc的顺序
  4. 在队列中移动一行时,例如在第3行和第4行之间移动,只需取这些行的update_at_utc的平均值,然后使用它来设置要移动的行的update_at_utc

注1:第4点假设插入和在队列中上下移动行的频率使得datetime2类型具有足够的分辨率。例如,如果您以1毫秒的间隔插入2行,然后尝试在这两行之间移动1000行,则datetime2分辨率将不足(https://docs.microsoft.com/en-us/sql/t-sql/data-types/datetime2-transact-sql?view=sql-server-2017)。在这种情况下,行在队列中的上/下移动将更加复杂。当将N行向下移动时,

  1. 记住第N行的update_at_utc向下
  2. 对于当前位置和新位置之间的所有行:将行update_at_utc分配给上一行update_at_utc
  3. 将行的update_at_utc分配到上面第1点中记住的日期。

注意2:为了避免在夏令时期间出现问题,我建议使用UTC日期而不是本地日期。