Псевдослучайная повторяемая сортировка в SQL Server (не NEWID () и не RAND ()) - PullRequest
12 голосов
/ 19 января 2009

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

Ответы [ 7 ]

13 голосов
/ 19 января 2009

Создание хеш-кода от 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 не обязательно)

1 голос
/ 30 января 2009
SELECT *, checksum(id) AS r FROM table ORDER BY r

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

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

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

1 голос
/ 19 января 2009

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

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

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

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

1 голос
/ 19 января 2009

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

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

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

0 голосов
/ 25 сентября 2009
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
0 голосов
/ 23 января 2009

В прошлом это работало хорошо для меня, и его можно применить к любой таблице (просто наберите в выражении ORDER BY):

SELECT *
FROM MY_TABLE
ORDER BY  
  (SELECT ABS(CAST(NEWID() AS BINARY(6)) % 1000) + 1);
0 голосов
/ 19 января 2009

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

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

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

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

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

...