web-dev-qa-db-fra.com

Enregistrement aléatoire à partir d'une table de base de données (T-SQL)

Existe-t-il un moyen succinct de récupérer un enregistrement aléatoire à partir d'une table de serveur SQL?

Je voudrais randomiser mes données de test unitaire, je cherche donc un moyen simple de sélectionner un identifiant aléatoire dans une table. En anglais, la sélection serait "Sélectionnez un identifiant dans la table où l'identifiant est un nombre aléatoire entre l'id le plus bas de la table et l'id le plus élevé de la table".

Je ne peux pas trouver un moyen de le faire sans avoir à exécuter la requête, tester une valeur nulle, puis réexécuter si null.

Des idées?

80
Jeremy

Existe-t-il un moyen succinct de récupérer un enregistrement aléatoire à partir d'une table de serveur SQL?

Oui

SELECT TOP 1 * FROM table ORDER BY NEWID()

Explication

Une NEWID() est générée pour chaque ligne et la table est ensuite triée par elle. Le premier enregistrement est renvoyé (c'est-à-dire l'enregistrement avec le GUID "le plus bas").

Remarques

  1. Les GUID sont générés sous forme de nombres pseudo-aléatoires depuis la version quatre:

    L'UUID de la version 4 est destiné à générer des UUID à partir de nombres vraiment aléatoires ou pseudo-aléatoires.

    L'algorithme est le suivant:

    • Réglez les deux bits les plus significatifs (bits 6 et 7) de clock_seq_hi_and_reserved à zéro et un, respectivement.
    • Définissez les quatre bits les plus significatifs (bits 12 à 15) du champ time_hi_and_version sur le numéro de version 4 bits de la section 4.1.3.
    • Réglez tous les autres bits sur des valeurs choisies de manière aléatoire (ou pseudo-aléatoire).

    n espace de noms URN IDentifier universel (UUID) - RFC 4122

  2. L'alternative SELECT TOP 1 * FROM table ORDER BY Rand() ne fonctionnera pas comme on pourrait le penser. Rand() renvoie une seule valeur par requête, donc toutes les lignes partageront la même valeur.

  3. Alors que les valeurs GUID sont pseudo-aléatoires, vous aurez besoin d'un meilleur PRNG pour les applications les plus exigeantes).

  4. Les performances typiques sont inférieures à 10 secondes pour environ 1 000 000 de lignes - bien sûr, selon le système. Notez qu'il est impossible d'atteindre un indice, les performances seront donc relativement limitées.

135
Sklivvz

Sur les tables plus grandes, vous pouvez également utiliser TABLESAMPLE pour éviter d’analyser la table entière.

SELECT  TOP 1 *
FROM YourTable
TABLESAMPLE (1000 ROWS)
ORDER BY NEWID()

Le ORDER BY NEWID est toujours requis pour éviter de renvoyer uniquement les lignes qui apparaissent en premier sur la page de données.

Le nombre à utiliser doit être choisi avec soin pour la taille et la définition de la table et vous pouvez envisager de réessayer la logique si aucune ligne n'est retournée. Le calcul derrière cela et pourquoi la technique n'est pas adaptée aux petites tables est discuté ici

23
Martin Smith

Essayez également votre méthode pour obtenir un Id aléatoire entre MIN (Id) et MAX (Id), puis

SELECT TOP 1 * FROM table WHERE Id >= @yourrandomid

Cela vous donnera toujours une rangée.

8
Sklivvz

Si vous souhaitez sélectionner des données volumineuses, la meilleure façon que je sache est:

SELECT * FROM Table1
WHERE (ABS(CAST(
    (BINARY_CHECKSUM
    (keycol1, NEWID())) as int))
    % 100) < 10

Source: MSDN

7
hmfarimani

Je cherchais à améliorer les méthodes que j'avais essayées et suis tombé sur ce post. Je me rends compte qu'elle est ancienne mais cette méthode n'est pas répertoriée. Je crée et applique des données de test; cela montre la méthode pour "adresse" dans un SP appelé avec @st (état à deux caractères)

Create Table ##TmpAddress (id Int Identity(1,1), street VarChar(50), city VarChar(50), st VarChar(2), Zip VarChar(5))
Insert Into ##TmpAddress(street, city, st, Zip)
Select street, city, st, Zip 
From tbl_Address (NOLOCK)
Where st = @st


-- unseeded Rand() will return the same number when called in rapid succession so
-- here, I seed it with a guaranteed different number each time. @@ROWCOUNT is the count from the most recent table operation.

Set @csr = Ceiling(Rand(convert(varbinary, newid())) * @@ROWCOUNT)

Select street, city, st, Right(('00000' + ltrim(Zip)),5) As Zip
From ##tmpAddress (NOLOCK)
Where id = @csr
0
user2788934