Pregunta

Me gustaría ordenar aleatoriamente un resultado de forma repetible para fines como la paginación. Para este NEWID () es demasiado aleatorio en que los mismos resultados no se pueden volver a obtener. Ordenar por Rand (semilla) sería ideal ya que con la misma semilla se obtendría la misma colección aleatoria. Desafortunadamente, el estado Rand () se restablece con cada fila, ¿alguien tiene una solución?

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

Nota, probé Rand (ID) pero resulta que está ordenado. Aparentemente Rand (n) & Lt; Rand (n + 1)

¿Fue útil?

Solución

A partir de la sugerencia de hash de gkrogers, esto funciona muy bien. ¿Alguna idea sobre el rendimiento?

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

EDITAR: tenga en cuenta que la declaración de @seed tal como se usa en la consulta podría reemplazarse con un parámetro o con un int constante si se utiliza SQL dinámico. (la declaración de @int en forma TSQL no es necesaria)

Otros consejos

Puede usar un valor de cada fila para reevaluar la función rand:

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

agregar la ID asegura que el rand se reinicialice para cada fila. Pero para un valor de semilla siempre obtendrá la misma secuencia de filas (siempre que la tabla no cambie)

Crear un hash puede llevar mucho más tiempo que crear un número aleatorio sembrado.

Para obtener más variación en el rendimiento de RAND ([semilla]) necesita hacer que la [semilla] varíe significativamente también. Posiblemente como ...

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

El uso de una constante garantiza la replicabilidad que solicitó. Pero tenga cuidado con el resultado de (id * 9999) causando un desbordamiento si espera que su tabla sea lo suficientemente grande ...

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

Este tipo de trabajos. Aunque el resultado de CheckSum () no me parece tan aleatorio. La Documentación de MSDN establece:

  

[...], no recomendamos usar CHECKSUM para detectar si los valores han cambiado, a menos que su aplicación pueda tolerar ocasionalmente un cambio. Considere usar HashBytes en su lugar. Cuando se especifica un algoritmo hash MD5, la probabilidad de que HashBytes devuelva el mismo resultado para dos entradas diferentes es mucho menor que la de CHECKSUM.

Pero puede ser más rápido.

Después de leer un poco, este es un método aceptado.

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

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

Tener id en la expresión hace que se vuelva a evaluar cada fila. Pero multiplicarlo por 0 asegura que no afecta el resultado de rand.

¡Qué horrible manera de hacer las cosas!

Esto me ha funcionado bien en el pasado, y se puede aplicar a cualquier tabla (simplemente atornille la 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 bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top