SQL Server 中的伪随机可重复排序(不是 NEWID() 和 RAND())

Posted

技术标签:

【中文标题】SQL Server 中的伪随机可重复排序(不是 NEWID() 和 RAND())【英文标题】:Pseudo Random Repeatable Sort in SQL Server (not NEWID() and not RAND()) 【发布时间】:2010-10-02 06:31:40 【问题描述】:

我想以可重复的方式对结果进行随机排序,以用于分页等目的。因为这个 NEWID() 太随机了,无法重新获得相同的结果。按 Rand(种子)排序将是理想的,因为使用相同的种子会产生相同的随机收集。不幸的是,Rand() 状态每行都会重置,有人有解决方案吗?

declare @seed as int;
set @seed = 1000;

create table temp (
id int,
date datetime)

insert into temp (id, date) values (1,'20090119')
insert into temp (id, date) values (2,'20090118')
insert into temp (id, date) values (3,'20090117')
insert into temp (id, date) values (4,'20090116')
insert into temp (id, date) values (5,'20090115')
insert into temp (id, date) values (6,'20090114')

-- re-seeds for every item
select *, RAND(), RAND(id+@seed) as r from temp order by r
--1 2009-01-19 00:00:00.000 0.277720118060575   0.732224964471124
--2 2009-01-18 00:00:00.000 0.277720118060575   0.732243597442382
--3 2009-01-17 00:00:00.000 0.277720118060575   0.73226223041364
--4 2009-01-16 00:00:00.000 0.277720118060575   0.732280863384898
--5 2009-01-15 00:00:00.000 0.277720118060575   0.732299496356156
--6 2009-01-14 00:00:00.000 0.277720118060575   0.732318129327415
-- Note how the last column is +=~0.00002

drop table temp

-- interestingly this works:
select RAND(@seed), RAND()
--0.732206331499865 0.306382810665955

注意,我尝试了 Rand(ID),但结果只是排序。显然兰德(n)

【问题讨论】:

【参考方案1】:
create table temp (
id int,
date datetime)

insert into temp (id, date) values (1,'20090119')
insert into temp (id, date) values (2,'20090118')
insert into temp (id, date) values (3,'20090117')
insert into temp (id, date) values (4,'20090116')
insert into temp (id, date) values (5,'20090115')
insert into temp (id, date) values (6,'20090114')

-- re-seeds for every item
select *, NEWID() r
from temp order by r

drop table temp

【讨论】:

【参考方案2】:
SELECT *, checksum(id) AS r FROM table ORDER BY r

这种作品。虽然 checksum() 的输出在我看来并不是那么随机。 MSDN Documentation 声明:

[...],我们不建议使用 CHECKSUM 来检测值是否已更改,除非您的应用程序可以容忍偶尔丢失更改。考虑改用 HashBytes。当指定MD5哈希算法时,HashBytes对两个不同的输入返回相同结果的概率远低于CHECKSUM。

但可能会更快。

【讨论】:

澄清了我的回答。但无论如何,这是您已经提出的解决方案。种。【参考方案3】:

以 gkrogers 哈希建议为基础,效果很好。对性能有什么想法吗?

declare @seed as int;
set @seed = 10;

create table temp (
id int,
date datetime)

insert into temp (id, date) values (1,'20090119')
insert into temp (id, date) values (2,'20090118')
insert into temp (id, date) values (3,'20090117')
insert into temp (id, date) values (4,'20090116')
insert into temp (id, date) values (5,'20090115')
insert into temp (id, date) values (6,'20090114')

-- re-seeds for every item
select *, HASHBYTES('md5',cast(id+@seed as varchar)) r
from temp order by r
--1 2009-01-19 00:00:00.000 0x6512BD43D9CAA6E02C990B0A82652DCA
--5 2009-01-15 00:00:00.000 0x9BF31C7FF062936A96D3C8BD1F8F2FF3
--4 2009-01-16 00:00:00.000 0xAAB3238922BCC25A6F606EB525FFDC56
--2 2009-01-18 00:00:00.000 0xC20AD4D76FE97759AA27A0C99BFF6710
--3 2009-01-17 00:00:00.000 0xC51CE410C124A10E0DB5E4B97FC2AF39
--6 2009-01-14 00:00:00.000 0xC74D97B01EAE257E44AA9D5BADE97BAF

drop table temp

编辑:注意,@seed 在查询中的声明可以用参数替换,如果使用动态 SQL,则可以用常量 int 替换。 (不需要以 TSQL 方式声明 @int)

【讨论】:

请问您试过我的方法了吗?它不需要任何额外的变量或存储过程。 我刚试过,不幸的是它没有提供可靠性。上面的声明只是为了提供一个完整的环境来复制问题。临时表要具有代表性。【参考方案4】:

这在过去对我来说效果很好,它可以应用于任何表(只需在 ORDER BY 子句上加上螺栓):

SELECT *
FROM MY_TABLE
ORDER BY  
  (SELECT ABS(CAST(NEWID() AS BINARY(6)) % 1000) + 1);

【讨论】:

它随机排序,但是,它不会给出可重复的结果。与 newid() 的行为相同?【参考方案5】:

创建哈希可能比创建种子随机数更耗时。

要在 RAND([seed]) 的输出中获得更多变化,您需要使 [seed] 也发生显着变化。可能是……

SELECT
    *,
    RAND(id * 9999)    AS [r]
FROM
   temp
ORDER BY
   r

使用常量可确保您要求的可复制性。但是如果您希望您的表足够大,请注意 (id * 9999) 导致溢出的结果...

【讨论】:

我认为这与上面的问题相同,其中值是递增的。 SELECT RAND(9999 * 1),RAND(9999 * 2),RAND(9999 * 3),RAND(9999 * 4),RAND(9999 * 5)0.899884439852407 0.0861955322535983 0.27250661186434 0.458817691475082 0.645128771085824”不是个t 增量,但也不是真正随机的... 你在哪个数据库上运行这个? SQL2008?【参考方案6】:

经过阅读,这是一种公认​​的方法。

Select Rand(@seed) -- now rand is seeded

Select *, 0 * id + Rand() as r from temp order by r

在表达式中包含 id 会导致每行重新计算它。但是将它乘以 0 确保它不会影响 rand 的结果。

这是多么可怕的做事方式!

【讨论】:

这对你有用吗?我得到一个恒定的 r 列。它还会产生额外的结果。也许它必须在 sp 内?虽然很疯狂,哇。 在我的测试中(在 SQL Server 2008 R2 上),RAND() 只被评估过一次。在什么条件下,它会评估出每一行中不同的东西?【参考方案7】:

您可以使用每一行中的一个值来重新评估 rand 函数:

Select *, Rand(@seed + id) as r from temp order by r

添加 ID 可确保为每一行重新播种 rand。但是对于种子的值,你总是会得到相同的行序列(假设表没有改变)

【讨论】:

谢谢JayArr。我确实尝试过这个,但不幸的是它最终被提升了。显然,第一个随机值是相当可预测的。我也会用该注释更新问题。

以上是关于SQL Server 中的伪随机可重复排序(不是 NEWID() 和 RAND())的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server - 随机随机播放结果,但为每条记录分配权重

浅析SQL Server数据库中的伪列以及伪列的含义

从不可排序的表 SQL Server 查询和导出

集合的伪随机遍历

集合的伪随机遍历

列排序是不是会影响 Microsoft SQL Server 2012 中的性能?