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

时间:2009-01-19 16:20:59

标签: sql sql-server tsql random paging

我想以可重复的方式对结果进行随机排序,以实现分页等目的。对于这个NEWID()太随机,因为无法重新获得相同的结果。兰德(种子)的排序将是理想的,因为相同的种子会产生相同的随机集合。不幸的是,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),但事实证明它已被排序。显然Rand(n)<兰特(N + 1)

7 个答案:

答案 0 :(得分:13)

建立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

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

答案 1 :(得分:1)

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

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

添加ID可确保每行重新使用rand。但对于种子值,您将始终返回相同的行序列(假设表不会更改)

答案 2 :(得分:1)

创建哈希比创建种子随机数要花费更多时间。

为了获得RAND([种子])输出的更多变化,您需要使[种子]显着变化。可能就是......

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

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

答案 3 :(得分:1)

SELECT *, checksum(id) AS r FROM table ORDER BY r

这种作品。虽然来自checksum()的输出看起来并不是随机的。 MSDN Documentation州:

  

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

但可能会更快。

答案 4 :(得分:0)

在做了一些阅读之后,这是一种可接受的方法。

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

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

在表达式中使用id会导致每行重新评估它。但是将它乘以0可确保它不会影响兰特的结果。

多么可怕的做事方式!

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

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