Псевдослучайная повторяемая сортировка в SQL Server (не NEWID () и не RAND ())

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

Вопрос

Я хотел бы случайным образом отсортировать результат для таких целей, как подкачка страниц. Для этого 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), но это просто оказалось отсортированным. Видимо Рэнд (n) & Lt; Rand (п + 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

РЕДАКТИРОВАТЬ: обратите внимание, объявление @seed, поскольку оно используется в запросе, может быть заменено параметром или константой int, если используется динамический SQL. (объявление @int в стиле TSQL не обязательно)

Другие советы

Вы можете использовать значение из каждой строки для переоценки функции rand:

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

добавление идентификатора обеспечивает повторное заполнение ранда для каждой строки. Но для значения seed вы всегда будете возвращать одну и ту же последовательность строк (при условии, что таблица не изменяется)

Создание хэша может занимать гораздо больше времени, чем создание случайного числа с заполнением.

Чтобы получить больше вариаций в производительности RAND ([семя]), вам также нужно значительно изменить [семя]. Возможно, такие как ...

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

Использование константы обеспечивает требуемую тиражируемость. Но будьте осторожны с результатом (id * 9999), вызывающим переполнение, если вы ожидаете, что ваша таблица станет достаточно большой ...

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

Этот вид работ. Хотя вывод контрольной суммы () не выглядит для меня настолько случайным. Документация MSDN гласит:

  

[...], мы не рекомендуем использовать CHECKSUM для определения того, изменились ли значения, если только ваше приложение не может терпеть случайное отсутствие изменений. Попробуйте вместо этого использовать HashBytes. Если указан алгоритм хеширования MD5, вероятность того, что HashBytes вернет один и тот же результат для двух разных входных данных, будет намного ниже, чем у CHECKSUM.

Но может быть, это быстрее.

После некоторого чтения это приемлемый метод.

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

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

Наличие идентификатора в выражении приводит к его переоценке для каждой строки. Но умножение на 0 гарантирует, что это не повлияет на результат ранда.

Какой ужасный способ делать вещи!

В прошлом это работало хорошо для меня, и его можно применить к любой таблице (просто добавьте предложение 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