Domanda

Vorrei ordinare casualmente un risultato in modo ripetibile per scopi come il paging. Per questo NEWID () è troppo casuale in quanto non è possibile ottenere nuovamente gli stessi risultati. L'ordine di Rand (seme) sarebbe l'ideale in quanto con lo stesso seme risulterebbe la stessa raccolta casuale. Sfortunatamente, lo stato di Rand () si reimposta ad ogni riga, qualcuno ha una soluzione?

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, ho provato Rand (ID) ma questo risulta essere ordinato. Apparentemente Rand (n) & Lt; Rand (n + 1)

È stato utile?

Soluzione

Costruire su suggerimento hash gkrogers funziona alla grande. Qualche idea sulla performance?

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: Nota, la dichiarazione di @seed come viene utilizzata nella query potrebbe essere sostituita con un parametro o con un int costante se si utilizza SQL dinamico. (la dichiarazione di @int in modo TSQL non è necessaria)

Altri suggerimenti

Puoi usare un valore per ogni riga per rivalutare la funzione rand:

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

l'aggiunta dell'ID garantisce che il rand venga ridimensionato per ogni riga. Ma per un valore di seed otterrai sempre la stessa sequenza di righe (a condizione che la tabella non cambi)

La creazione di un hash può richiedere molto più tempo rispetto alla creazione di un numero casuale con seeding.

Per ottenere più variazioni nell'output di RAND ([seed]) è necessario anche variare significativamente [seed]. Forse come ...

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

L'uso di una costante garantisce la replicabilità richiesta. Ma fai attenzione al risultato di (id * 9999) che provoca un overflow se ti aspetti che il tuo tavolo diventi abbastanza grande ...

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

Questo tipo di opere. Sebbene l'output di checksum () non mi sembri affatto casuale. La Documentazione MSDN afferma:

  

[...], non è consigliabile utilizzare CHECKSUM per rilevare se i valori sono stati modificati, a meno che l'applicazione non possa tollerare occasionalmente una modifica. Prendi invece in considerazione l'utilizzo di HashBytes. Quando viene specificato un algoritmo hash MD5, la probabilità che HashBytes restituisca lo stesso risultato per due input diversi è molto inferiore a quella di CHECKSUM.

Ma potrebbe essere più veloce.

Dopo aver letto un po ', questo è un metodo accettato.

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

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

Avere ID nell'espressione fa rivalutare ogni riga. Ma moltiplicandolo per 0 si assicura che non influisca sul risultato di Rand.

Che modo orribile di fare le cose!

Questo ha funzionato bene per me in passato e può essere applicato a qualsiasi tabella (basta inserire la clausola 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
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top