Question

Je souhaite trier un résultat de manière aléatoire, de manière répétable, à des fins telles que la pagination. Pour cette NEWID () est trop aléatoire en ce que les mêmes résultats ne peuvent pas être obtenus. Ordre par Rand (graine) serait idéal car avec la même graine, le même prélèvement aléatoire en résulterait. Malheureusement, l'état Rand () est réinitialisé à chaque ligne. Quelqu'un a-t-il une solution?

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

Remarque, j'ai essayé Rand (ID) mais cela se révèle être trié. Apparemment, Rand (n) & Lt; Rand (n + 1)

Était-ce utile?

La solution

En se basant sur la suggestion de gkrogers, cela fonctionne très bien. Des réflexions sur les performances?

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: Remarque: la déclaration de @seed telle qu’elle est utilisée dans la requête peut être remplacée par un paramètre ou par une constante int si du SQL dynamique est utilisé. (la déclaration de @int à la TSQL n'est pas nécessaire)

Autres conseils

Vous pouvez utiliser une valeur de chaque ligne pour réévaluer la fonction rand:

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

l'ajout de l'ID garantit que le rand est réensemencé pour chaque ligne. Mais pour une valeur de graine, vous obtiendrez toujours la même séquence de lignes (à condition que la table ne change pas)

La création d'un hachage peut prendre beaucoup plus de temps que la création d'un nombre aléatoire initial.

Pour obtenir plus de variation dans la valeur de RAND ([graine]), vous devez également faire varier la [graine] de manière significative. Peut-être comme ...

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

L'utilisation d'une constante garantit la réplicabilité demandée. Mais faites attention au résultat de (id * 9999), provoquant un débordement si vous vous attendez à ce que votre table devienne assez grande ...

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

Ce genre de travaux. Bien que la sortie de checksum () ne me semble pas si aléatoire. La la documentation MSDN indique:

  

[...], il est déconseillé d'utiliser CHECKSUM pour détecter si des valeurs ont été modifiées, à moins que votre application ne puisse tolérer de temps en temps une modification manquante. Pensez à utiliser HashBytes à la place. Lorsqu'un algorithme de hachage MD5 est spécifié, la probabilité que HashBytes renvoie le même résultat pour deux entrées différentes est bien inférieure à celle de CHECKSUM.

Mais peut-être est-ce plus rapide.

Après quelques lectures, cette méthode est acceptée.

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

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

Le fait d'avoir un identifiant dans l'expression entraîne sa réévaluation chaque ligne. Mais le multiplier par 0 garantit que cela n’affectera pas le résultat du rand.

Quelle horrible façon de faire les choses!

Cela a bien fonctionné pour moi dans le passé et il peut être appliqué à n'importe quelle table (il suffit d'insérer la clause 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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top