14

我想以可重复的方式对结果进行随机排序,以用于分页等目的。因为这个 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) < 兰德(n+1)

4

7 回答 7

15

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

于 2009-01-19T16:42:15.227 回答
1

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

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

添加 ID 可确保为每一行重新播种 rand。但是对于种子的值,您将始终返回相同的行序列(前提是表没有更改)

于 2009-01-19T16:28:55.503 回答
1

创建散列可能比创建种子随机数更耗时。

要获得 RAND([seed]) 输出的更多变化,您需要使 [seed] 也有显着变化。可能比如...

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

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

于 2009-01-19T18:45:54.963 回答
1
SELECT *, checksum(id) AS r FROM table ORDER BY r

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

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

但可能会更快。

于 2009-01-30T15:36:19.427 回答
0

在做了一些阅读之后,这是一种公认​​的方法。

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

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

在表达式中有 id 会导致它在每一行都被重新评估。但是将它乘以 0 确保它不会影响 rand 的结果。

多么可怕的做事方式!

于 2009-01-19T16:45:39.557 回答
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
于 2009-09-24T23:17:02.083 回答
-1

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

SELECT *
FROM MY_TABLE
ORDER BY  
  (SELECT ABS(CAST(NEWID() AS BINARY(6)) % 1000) + 1);
于 2009-01-23T20:28:59.247 回答