Pergunta

Gostaria de forma aleatória tipo resultado de uma forma repetível para fins tais como paginação. Para este NEWID () é muito aleatória em que os mesmos resultados não podem ser obtidos re-. Ordenar por Rand (semente) seria ideal como com a mesma semente da mesma coleção aleatória resultaria. Infelizmente, o Rand () redefine estaduais com cada linha, alguém tem uma solução?

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

Note, eu tentei Rand (ID), mas que apenas acaba por ser resolvido. Aparentemente Rand (n)

Foi útil?

Solução

Construção off de gkrogers sugestão de hash isso funciona muito bem. Quaisquer pensamentos sobre o desempenho?

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: Note, a declaração de @seed como é uso na consulta pode ser substitua por um parâmetro ou com um int constante se SQL dinâmico é usado. (Declaração de @int de forma TSQL não é necessário)

Outras dicas

Você pode usar um valor de cada linha para re-avaliar a função rand:

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

adicionar o ID garante que o rand é reseeded para cada linha. Mas, para um valor de semente que você vai sempre voltar a mesma seqüência de linhas (desde que a tabela não muda)

A criação de um hash pode ser muito mais demorado do que a criação de um número aleatório semeado.

Para obter mais variação no ourput da RAND ([semente]) que você precisa para tornar a [semente] variar significativamente também. Possivelmente, como ...

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

Usando uma constante garante a replicabilidade você pediu. Mas tenha cuidado com o resultado de (id * 9999) causando um estouro se você espera sua mesa para obter grande o suficiente ...

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

Este tipo de obras. Embora a saída da soma de verificação () não parece tão aleatória para mim. A MSDN Documentação estados:

[...], nós não recomendamos o uso de soma de verificação para detectar se valores foram alterados, a menos que a sua aplicação pode tolerar ocasionalmente faltando uma mudança. Considere o uso de HashBytes vez. Quando um algoritmo de hash MD5 é especificado, a probabilidade de HashBytes retornam o mesmo resultado para duas entradas diferentes é muito menor do que a soma de verificação.

Mas pode ser mais rápido.

Depois de fazer algumas lendo este é um método aceitável.

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

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

Tendo id na expressão faz com que seja reavaliada a cada linha. Mas multiplicando-o por 0 garante que ele não não afeta o resultado do rand.

Que maneira horrível de fazer as coisas!

Isso tem funcionado bem para mim no passado, e ele pode ser aplicado a qualquer tabela (basta prender a cláusula 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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top