SQL Serverの擬似ランダム繰り返し可能ソート(NEWID()およびRAND()ではありません)

StackOverflow https://stackoverflow.com/questions/458175

質問

ページングなどの目的で、結果を繰り返し可能な方法でランダムにソートしたいと思います。このため、同じ結果を再取得できないという点で、NEWID()はランダムすぎます。 Rand(seed)による順序付けは、同じシードを使用すると同じランダムコレクションが生成されるため理想的です。残念ながら、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)<!> lt;ランド(n + 1)

役に立ちましたか?

解決

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

EDIT:クエリで使用されている@seedの宣言は、動的SQLが使用されている場合、パラメータまたは定数intに置き換えることができます。 (TSQL形式で@intを宣言する必要はありません)

他のヒント

各行の値を使用して、rand関数を再評価できます。

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

IDを追加すると、各行でランドが再シードされます。ただし、シードの値については、常に同じ行のシーケンスが返されます(テーブルが変更されない場合)

ハッシュの作成は、シードされた乱数を作成するよりもはるかに時間がかかります。

RAND([seed])の出力のバリエーションを増やすには、[seed]も大幅に変化させる必要があります。おそらく...

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

定数を使用すると、要求された複製可能性が保証されます。ただし、テーブルが十分に大きくなると予想される場合は、(id * 9999)の結果がオーバーフローを引き起こすことに注意してください...

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

この種の作品。 checksum()からの出力は、私にはそれほどランダムに見えませんが。 MSDNドキュメントの状態:

  

[...]、CHECKSUMを使用して値が変更されたかどうかを検出することはお勧めしません。ただし、アプリケーションが変更の欠落を許容できない場合があります。代わりにHashBytesの使用を検討してください。 MD5ハッシュアルゴリズムが指定されている場合、HashBytesが2つの異なる入力に対して同じ結果を返す確率は、CHECKSUMの確率よりもはるかに低くなります。

しかし、もっと速いかもしれません。

いくつかの読書をした後、これは受け入れられた方法です。

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

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

式にidがあると、行ごとに再評価されます。ただし、0を乗算すると、randの結果に影響しないことが保証されます。

なんて恐ろしいことをするのか!

これは私にとって過去にうまく機能しており、どのテーブルにも適用できます(ORDER BY句をボルトで固定するだけです):

SELECT *
FROM MY_TABLE
ORDER BY  
  (SELECT ABS(CAST(NEWID() AS BINARY(6)) % 1000) + 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
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top