简体   繁体   English

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

[英]How to create a queue like structure in SQL Server

Is there a good way to create a queue like structure in SQL Server? 有没有在SQL Server中创建类似队列的好方法?

Requirements: 要求:

  1. When I insert rows, I want them to default to the bottom of the queue 当我插入行时,我希望它们默认为队列底部
  2. When I select rows, I want to easily be able to get the top of the queue 选择行时,我希望能够轻松地到达队列的顶部
  3. Here's the tough one: I want to be able to easily move something up the queue, and reorient the rest. 这是一个棘手的问题:我希望能够轻松地将某些内容移到队列中,并重新调整其余部分的方向。 Example: move item 5 up to number 1, then 1-4 becomes 2-5 示例:将项目5移至数字1,然后1-4变为2-5

A simple identity column would work for requirements 1 and 2, but how would I handle 3? 一个简单的标识列适用于需求1和2,但是我将如何处理3?

Solution

I ended up implementing the solution from @roger-wolf One difference, I used a trigger rather than a stored procedure to renumber. 我最终从@ roger-wolf实施了解决方案。一个区别是,我使用触发器而不是存储过程来重新编号。 Here's my trigger code: 这是我的触发代码:

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

This works well for me as the queue is always relatively small and infrequently updated, so I don't have to work about performance of the trigger. 这对我来说效果很好,因为队列总是相对较小且不经常更新,因此我不必处理触发器的性能。

Use a float column for prioritisation and an approach similar to Celko trees: 使用float列进行优先级排序,并使用类似于Celko树的方法:

  • If you have items with priorities 1, 2, and 3 and the last needs to become second, calculate an average between its new neighbours, 1.5 in this example; 如果您有优先级为1、2和3的项目,而最后一个需要排在第二位,则计算其新邻居之间的平均值,在此示例中为1.5;
  • If another one needs to become second, its priority would be 1.25. 如果另一个需要成为第二,则其优先级为1.25。 This can go on for quite a while; 这可以持续一段时间。
  • When displaying queued items by their priority, use row_number() instead of float values in UI; 当按优先级显示排队的项目时,在UI中使用row_number()而不是float值;
  • If items become too close together (say, 1e-10 or less), have a stored procedure ready to renumber them as integers. 如果项目之间的距离太近(例如1e-10或更小),请准备一个存储过程以将它们重新编号为整数。

The only deficiency I see here is that it becomes a bit more difficult to find N-th item in a middle of a queue, when it's neither first nor last. 我在这里看到的唯一缺陷是,当队列中的第N个项目既不是第一个也不是最后一个时,要找到它要困难一些。 If you don't need that, the approach should work. 如果您不需要它,则该方法应该有效。

您可以添加类型为DateTimePriority列,然后将某行设置为优先级行时,在Priority列中设置当前日期时间,然后order by条件将其用作order by一部分?

I had a similar requirement in a past project, what I did (and it worked): 我在过去的项目中有类似的要求,我做了什么(并且奏效了):

  1. Add column update_at_utc of type datetime2 添加类型为datetime2 update_at_utc
  2. When inserting, set update_at_utc = GETDATEUTC() 插入时,设置update_at_utc = GETDATEUTC()
  3. When retrieving, order by update_at_utc 检索时,请按update_at_utc排序
  4. When moving a row in the queue, for example between rows 3 and 4, simply take average of update_at_utc of these rows and use it to set update_at_utc of the row being moved. 在队列中移动一行时,例如在第3行和第4行之间移动时,只需取这些行的update_at_utc的平均值,然后使用它来设置要移动的行的update_at_utc

Note 1: Point 4 assumes that the frequency of inserts and of moving the rows up/down the queue is such that datetime2 type has sufficient resolution. 注1:第4点假设插入和上下移动队列的频率使得datetime2类型具有足够的分辨率。 For example, if you insert 2 rows 1 millisecond apart, and then try to move 1000 rows between these 2 rows, then datetime2 resolution will be insufficient ( https://docs.microsoft.com/en-us/sql/t-sql/data-types/datetime2-transact-sql?view=sql-server-2017 ). 例如,如果您以1毫秒的间隔插入2行,然后尝试在这2行之间移动1000行,则datetime2分辨率将不足( https://docs.microsoft.com/zh-cn/sql/t-sql / data-types / datetime2-transact-sql?view = sql-server-2017 )。 In such case, the moving of rows up/down the queue would need to be more complicated; 在这种情况下,行在队列中的上/下移动将更加复杂。 When moving a row N places lower down: 当移动N行时,请降低位置:

  1. Remember update_at_utc of the row N places lower down 请记住,第N行的update_at_utc位置较低
  2. For all rows between the current and the new position: assign row's update_at_utc to the preceding row's update_at_utc 对于当前位置和新位置之间的所有行:将行的update_at_utc分配给上一行的update_at_utc
  3. Assign update_at_utc of the row being moved to the date remembered in point 1 above. 将要移动的行的update_at_utc分配到上面第1点中记住的日期。

Note 2: I suggest UTC dates instead of local dates to avoid issues during a daylight saving switch. 注意2:我建议使用UTC日期而不是本地日期,以避免在夏令时期间出现问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM