【问题标题】:Random order set to certain date随机订单设置为特定日期
【发布时间】:2021-06-18 16:21:43
【问题描述】:

有没有办法对事件列表进行随机排序并让它只在当天保持这种状态?涉及分页,因此缓存并不是一个好的选择。

我知道我可以通过NEWID() 进行随机排序,但如果它从未被缓存并且在第 1 页包含相同的项目,那将在第 100 页显示不同的顺序。如果是按日期,那么它将显示排序,不管页面缓存,第二天重置。

    WITH PagedResults AS 
        (
            SELECT CASE WHEN @SortOrder = 'Name' AND @SortDirection = 'DESC' THEN ROW_NUMBER() OVER (ORDER BY ep.Name DESC, ev.Id ASC)
            ELSE ROW_NUMBER() OVER (ORDER BY ev.Id ASC) END  AS [Row],
            CASE WHEN @SortOrder = 'Name' AND @SortDirection = 'DESC' THEN ROW_NUMBER() OVER (ORDER BY ep.Name ASC, ev.Id DESC)
            ELSE ROW_NUMBER() OVER (ORDER BY ev.Id DESC)
            END  AS [RowReverse],
                ev.Id,
                ep.Name,
    )
        SELECT 
                [Row] + RowReverse - 1 AS Total, 
                [Row],
                [RowReverse],
                Id, 
                Name,
        FROM PagedResults
        WHERE [Row] BETWEEN (((@Page - 1) * @PageSize) + 1) AND (@Page * @PageSize)
        ORDER BY [Row], [RowReverse] DESC

【问题讨论】:

  • 您必须生成随机订单,然后将其临时存储在某个地方。我想您可以将随机顺序转换为秒并将其作为缓存形式添加到当前日期时间。
  • 您可以生成某种由固定日期部分和随机后缀组成的计算哈希列。这是你要找的吗?
  • 如果您使用RAND() 进行随机排序,则可以使用日期作为种子。那么当天的随机数序列将是相同的。不过可能效率不高。
  • 如果有人插入或删除一行,你建议发生什么?顺便说一下,我建议你看看Keyset Pagination,它比行集分页效率高得多
  • 是否可以在表格中添加额外的列?

标签: sql sql-server random sql-order-by


【解决方案1】:

下面我将使用表sortme定义为:

CREATE TABLE sortme
             (id integer
                 IDENTITY (1, 1),
              PRIMARY KEY(id));

ORDER BY rand(<seed>) 与从当天派生的<seed> 结合使用可能是第一个想到的方法。

DECLARE @today datetime = '2021-01-01 01:01:01';

SELECT *
       FROM sortme
       ORDER BY rand(convert(integer,
                             convert(varchar(8),
                                     @today,
                                     112)));

rand() 保证生成的值相同,如果<seed> 相同。所以我们检查了这一点。有点……因为每一行的值也相同。因此,将rand(<seed>) 用于固定的<seed> 将实际上什么都不做。

db<>fiddle

我们可以尝试通过将随机值绑定到结果的键来克服这个问题。 day 和 key 的随机值,即 key 标识的行,在一天内是稳定的。

    DECLARE @today datetime = '2021-01-01 01:01:01';
    SELECT *
           FROM sortme
           ORDER BY rand(id * 100000000
                         + 
                         convert(integer,
                                 convert(varchar(8),
                                         @today,
                                         112)));

但不幸的是,每个(简单)公式将键和我想出的日期结合起来确实产生了足够接近每一天的随机值,因此每天的顺序方式都相同。它没有很好地分散。

db<>fiddle

现在,如果我们考虑一下,我们需要一个函数,它可以很好地分散单调递增的值。这就是哈希函数的领域。

所以我们可以使用hashbytes()

DECLARE @today datetime = '2021-01-01 01:01:01';

SELECT *
       FROM sortme
       ORDER BY hashbytes('SHA2_512',
                          concat(convert(varchar(8),
                                         @today,
                                         112),
                                 id));

哈希将在一天中保持稳定,并且一行的键只取决于这些值。并且(某些)散列函数分散良好的事实使结果“看起来很随机”。

db<>fiddle

从表中删除或插入到表中当然会导致行在其他“旧”行或剩余行之间的某处出现或消失。但是,如果我们使用的键是稳定的,比如不会填补空白的自动增量,那么“旧”行或剩余行之间的顺序无论如何都会在一天中保持稳定。

当然,请注意,就不可预测而言,这不是随机的!

但我认为它的目的只是改善用户体验,每天呈现一个“感觉”随机的结果,以使内容更加有趣和令人兴奋。应该够用了。

【讨论】:

  • 仅供参考,SQL Server 中的rand(convert(integer, convert(varchar(8), @today, 112))) 在您的第一个查询中为每一行返回相同的数字。每行的种子相同,每行的RAND 结果相同。
  • 我打算提出类似的建议,但使用BINARY_CHECKSUM。我想这对于计算值和之后进行排序的资源要求都不那么高,因为它只是一个返回的整数。缺点可能是它有更高的碰撞机会,但我做了一个 id 范围从 1 到 5M 的测试并且没有双打。 ORDER BY BINARY_CHECKSUM(Convert(date, CURRENT_TIMESTAMP), id)
  • @VladimirBaranov:谢谢!我一定是在路上的某个地方弄糊涂了……我相应地编辑了答案。
  • @deroby:这可能是一个很好的建议,我很想编辑它。但是当我测试它时,顺序对我来说并不太随机。很大一部分似乎只是按(自动增量)键排序,几天看起来一样:dbfiddle.uk/…我是否误解了您的建议并犯了错误?
  • @stickybit 不,你做得很好,但似乎我(大大)高估了BINARY_CHECKSUM() 的力量。当您调转dateid 参数时,它会稍微好一些,但恕我直言仍然不够好。这应该教会我不要先尝试 =P 无论如何,为了缓解我尝试先添加一个“盐”值 - 只是一个字符串 - 如果它足够长,它会减少它的坏处,但仍然不是很好。最好坚持我猜的哈希值。见:dbfiddle.uk/…
猜你喜欢
  • 1970-01-01
  • 2015-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多