web-dev-qa-db-fra.com

Comment générer un nombre aléatoire pour chaque ligne dans une sélection SQL?

J'ai besoin d'un nombre aléatoire différent pour chaque ligne de ma table. Le code apparemment évident suivant utilise la même valeur aléatoire pour chaque ligne.

SELECT table_name, Rand() magic_number 
FROM information_schema.tables 

Je voudrais obtenir un INT ou un FLOAT de cela. Le reste de l'histoire est que je vais utiliser ce nombre aléatoire pour créer une date aléatoire décalée par rapport à une date connue, par exemple. 1-14 jours décalés à partir d'une date de début.

Ceci est pour Microsoft SQL Server 2000.

302
MatthewMartin

Jetez un oeil à SQL Server - Définir des nombres aléatoires qui a une explication très détaillée.

Pour résumer, le code suivant génère un nombre aléatoire compris entre 0 et 13 inclus avec une distribution normalisée:

ABS(CHECKSUM(NewId())) % 14

Pour changer votre gamme, il suffit de changer le nombre à la fin de l'expression. Faites particulièrement attention si vous avez besoin d’une plage comprenant à la fois des nombres positifs et des nombres négatifs. Si vous le faites mal, il est possible de compter deux fois le nombre 0.

Petit avertissement pour les fous de maths dans la salle: il y a un très léger biais dans ce code. CHECKSUM() donne des chiffres uniformes sur toute la plage du type de données sql Int, ou au moins aussi près que le permet mon test (l'éditeur). Cependant, il y aura un certain biais lorsque CHECKSUM () produira un nombre tout à fait à la fin de cette plage. Chaque fois que vous obtenez un nombre compris entre le nombre entier maximal possible et le dernier multiple exact de la taille de votre plage désirée (14 dans ce cas) avant ce nombre entier maximum, ces résultats sont privilégiés par rapport à la partie restante de votre plage qui ne peut pas être produite. ce dernier multiple de 14.

Par exemple, imaginez que la plage entière du type Int ne soit que de 19. 19 est le plus grand entier possible que vous puissiez contenir. Lorsque CHECKSUM () donne 14-19, ceux-ci correspondent aux résultats 0-5. Ces nombres seraient fortement privilégiés par rapport à 6-13, car CHECKSUM () est deux fois plus susceptible de les générer. C'est plus facile de démontrer cela visuellement. Vous trouverez ci-dessous l'ensemble des résultats possibles pour notre plage entière imaginaire:

 Checksum Entier: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
 Plage Résultat: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 0 1 2 3 4 5 

Vous pouvez voir ici qu'il y a plus de chances de produire certains chiffres que d'autres: le biais. Heureusement, la plage réelle du type Int est beaucoup plus grande ... à tel point que dans la plupart des cas, le biais est presque indétectable. Cependant, il faut en prendre conscience si vous vous retrouvez déjà confronté à un problème de sécurité grave.

476
SQLMenace

Lorsqu'il est appelé plusieurs fois dans un même lot, Rand () renvoie le même numéro.

Je suggérerais d'utiliser convert (varbinary, newid()) comme argument de départ:

SELECT table_name, 1.0 + floor(14 * Rand(convert(varbinary, newid()))) magic_number 
FROM information_schema.tables

Il est garanti que newid() renverra une valeur différente à chaque appel, même au sein du même lot. Son utilisation en tant que valeur de départ donnera alors Prompt Rand () pour donner une valeur différente à chaque fois.

Édité pour obtenir un nombre entier aléatoire de 1 à 14.

89
Jeremy Smyth
Rand(CHECKSUM(NEWID()))

Ce qui précède générera un nombre (pseudo) aléatoire compris entre 0 et 1, exclusif. S'il est utilisé dans une sélection, étant donné que la valeur de départ change pour chaque ligne, un nouveau nombre aléatoire sera généré pour chaque ligne (il n'est toutefois pas garanti de générer un nombre unique par ligne).

Exemple combiné avec une limite supérieure de 10 (produit les nombres 1 à 10):

CAST(Rand(CHECKSUM(NEWID())) * 10 as INT) + 1

Documentation Transact-SQL:

  1. CAST(): https://docs.Microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql
  2. Rand(): http://msdn.Microsoft.com/en-us/library/ms177610.aspx
  3. CHECKSUM(): http://msdn.Microsoft.com/en-us/library/ms189788.aspx
  4. NEWID(): https://docs.Microsoft.com/en-us/sql/t-sql/functions/newid-transact-sql
66
Aaron Hoffman

Génération de nombres aléatoires entre 1000 et 9999 inclus:

FLOOR(Rand(CHECKSUM(NEWID()))*(9999-1000+1)+1000)

"+1" - pour inclure les valeurs supérieures (9999 pour l'exemple précédent)

35
Vova

Répondre à l'ancienne question, mais cette réponse n'a pas été fournie auparavant, et j'espère que cela sera utile pour quelqu'un qui trouvera ces résultats via un moteur de recherche.

Avec SQL Server 2008, une nouvelle fonction a été introduite, CRYPT_GEN_RANDOM(8), qui utilise CryptoAPI pour produire un nombre aléatoire fort sur le plan cryptographique, renvoyé sous la forme VARBINARY(8000). Voici la page de documentation: https://docs.Microsoft.com/en-us/sql/t-sql/functions/crypt-gen-random-transact-sql

Donc, pour obtenir un nombre aléatoire, vous pouvez simplement appeler la fonction et la convertir en un type:

select CAST(CRYPT_GEN_RANDOM(8) AS bigint)

ou pour obtenir un float compris entre -1 et +1, vous pouvez faire quelque chose comme ceci:

select CAST(CRYPT_GEN_RANDOM(8) AS bigint) % 1000000000 / 1000000000.0
19
Andrei Tanas

La fonction Rand () générera le même nombre aléatoire si elle est utilisée dans une requête SELECT de la table. La même chose s'applique si vous utilisez une graine pour la fonction Rand. Une autre façon de le faire, utilise ceci:

SELECT ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) AS [RandomNumber]

J'ai eu l'information de ici , ce qui explique très bien le problème.

11
MicSim

Si vous devez conserver votre graine afin qu'elle génère les "mêmes" données aléatoires à chaque fois, vous pouvez procéder comme suit:

1. Créer une vue qui retourne select Rand ()

if object_id('cr_sample_randView') is not null
begin
    drop view cr_sample_randView
end
go

create view cr_sample_randView
as
select Rand() as random_number
go

2. Créez un fichier UDF qui sélectionne la valeur dans la vue.

if object_id('cr_sample_fnPerRowRand') is not null
begin
    drop function cr_sample_fnPerRowRand
end
go

create function cr_sample_fnPerRowRand()
returns float
as
begin
    declare @returnValue float
    select @returnValue = random_number from cr_sample_randView
    return @returnValue
end
go

. Avant de sélectionner vos données, définissez la fonction Rand (), puis utilisez l'UDF dans votre instruction select.

select Rand(200);   -- see the Rand() function
with cte(id) as
(select row_number() over(order by object_id) from sys.all_objects)
select 
    id,
    dbo.cr_sample_fnPerRowRand()
from cte
where id <= 1000    -- limit the results to 1000 random numbers
5
Mitselplik

Avez-vous une valeur entière dans chaque ligne que vous pourriez transmettre en tant que graine à la fonction Rand?

Pour obtenir un entier compris entre 1 et 14, je crois que cela fonctionnerait:

FLOOR( Rand(<yourseed>) * 14) + 1
5
CoderDennis
select round(Rand(checksum(newid()))*(10)+20,2)

Ici, le nombre aléatoire sera compris entre 20 et 30. round donnera un maximum de deux décimales.

Si vous voulez des nombres négatifs, vous pouvez le faire avec

select round(Rand(checksum(newid()))*(10)-60,2)

Ensuite, la valeur minimale sera -60 et la valeur maximale sera -50.

4
Tirthankar

essayez d'utiliser une valeur de départ dans le Rand (seedInt). Rand () ne s’exécutera qu’une fois par instruction, c’est pourquoi vous voyez le même nombre à chaque fois.

4
northpole

Si vous n'avez pas besoin que ce soit un entier, mais n'importe quel identifiant unique aléatoire, vous pouvez utiliser newid()

SELECT table_name, newid() magic_number 
FROM information_schema.tables
4
Peter Cooper Jr.
4
David
select ABS(CAST(CAST(NEWID() AS VARBINARY) AS INT)) as [Randomizer]

a toujours travaillé pour moi

2
theteague

sélectionnez newid ()

ou éventuellement cette sélection binary_checksum (newid ())

2
Chris Klepeis

C'est aussi simple que:

DECLARE @rv FLOAT;
SELECT @rv = Rand();

Et cela mettra un nombre aléatoire entre 0 et 99 dans un tableau:

CREATE TABLE R
(
    Number int
)

DECLARE @rv FLOAT;
SELECT @rv = Rand();

INSERT INTO dbo.R
(Number)
    values((@rv * 100));

SELECT * FROM R
2
Rosjier Hall

Le problème que j'ai parfois avec la "Réponse" sélectionnée est que la distribution n'est pas toujours uniforme. Si vous avez besoin d'une distribution très uniforme de nombres aléatoires de 1 à 14 parmi un grand nombre de lignes, vous pouvez procéder de la sorte bien):

SELECT table_name, ntile(14) over(order by newId()) randomNumber 
FROM information_schema.tables

Cela fait l’opposé des solutions aléatoires normales en ce sens qu’il garde les nombres séquencés et rend aléatoire l’autre colonne.

Rappelez-vous que ma base de données contient 511 tables (ce qui n’est pertinent que si nous sélectionnons à partir de information_schema). Si je prends la requête précédente et la mets dans une table temporaire #X, puis lance cette requête sur les données résultantes:

select randomNumber, count(*) ct from #X
group by randomNumber

J'obtiens ce résultat en me montrant que mon nombre aléatoire est TRÈS uniformément réparti sur les nombreuses lignes:

enter image description here

2
Trevor
    DROP VIEW IF EXISTS vwGetNewNumber;
    GO
    Create View vwGetNewNumber
    as
    Select CAST(Rand(CHECKSUM(NEWID())) * 62 as INT) + 1 as NextID,
    'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'as alpha_num;

    ---------------CTDE_GENERATE_PUBLIC_KEY -----------------
    DROP FUNCTION IF EXISTS CTDE_GENERATE_PUBLIC_KEY;  
    GO
    create function CTDE_GENERATE_PUBLIC_KEY()
    RETURNS NVARCHAR(32)
    AS 
    BEGIN
        DECLARE @private_key NVARCHAR(32);
        set @private_key = dbo.CTDE_GENERATE_32_BIT_KEY();
        return @private_key;
    END;
    go

---------------CTDE_GENERATE_32_BIT_KEY -----------------
DROP FUNCTION IF EXISTS CTDE_GENERATE_32_BIT_KEY;  
GO
CREATE function CTDE_GENERATE_32_BIT_KEY()
RETURNS NVARCHAR(32)
AS 
BEGIN
    DECLARE @public_key NVARCHAR(32);
    DECLARE @alpha_num NVARCHAR(62);
    DECLARE @start_index INT = 0;
    DECLARE @i INT = 0;
    select top 1 @alpha_num = alpha_num from vwGetNewNumber;
        WHILE @i < 32
        BEGIN
          select top 1 @start_index = NextID from vwGetNewNumber;
          set @public_key = concat (substring(@alpha_num,@start_index,1),@public_key);
          set @i = @i + 1;
        END;
    return @public_key;
END;
    select dbo.CTDE_GENERATE_PUBLIC_KEY() public_key;
1
ichak khoury

Mettre à jour ma_table set my_field = CEILING ((Rand (CAST (NEWID () AS varbinary))) * 10))

Nombre compris entre 1 et 10.

0
user3478586