Limitations de la fonction RAND() de SQL Server [INFO] - SQL/NoSQL - Programmation
Marsh Posté le 05-09-2005 à 13:00:39
ReplyMarsh Posté le 05-09-2005 à 13:14:46
ah oui, en fait tu t'es pas mal pris la tete,
tu peux aussi faire ça :
select rand(checksum(NEWID()))
Marsh Posté le 06-09-2005 à 16:25:07
en fait, le souci, c'est que tout comme le truc que j'avais fait, on n'a pas le droit de l'utiliser dans une fonction.
sinon, un bête newid() suffit même
Marsh Posté le 06-09-2005 à 16:25:30
mais ceci dit, je suis arrivé au même résultat, donc c'est pas mieu
Marsh Posté le 10-06-2005 à 22:58:14
Salut,
Comme beaucoup de personnes qui ont bossé avec SQL Server, j'ai été confronté au problème de la génération d'un nombre aléatoire.
La fonction RAND() de T-SQL fait tout sauf de l'aléatoire, et c'est facilement vérifiable, y compris avec l'exemple de la documentation.
Déjà, au sein d'une même requête, RAND() produit un nombre unique pour toutes les lignes retournées, ce qui est loin d'être utile.
Ensuite, entre deux appels, quelque soit le temps qui se déroule entre eux, si on utilise le même SEED, on se retrouve avec une valeur quelque peut... identique. Le changement est perceptible (quand il l'est) à partir de la 4° ou 5° décimale... C'est peu.
Bref, cette fonction n'est vraiment pas utilisable.
Pourtant, SQL Server dispose d'un autre générateur de nombre aléatoires, mais il n'est pas vraiment utilisable... Parcequ'il ne retourne pas de nombre compris entre 0 et 1, mais un GUID (Identificateur global unique), qui est un nombre de 16... bytes, pas bits ! (128 bits).
Déjà, ça fait un peux gros, et en plus, c'est au format peut utilisable pour faire des calculs (que le processeur ne saurait de toute façon pas faire) : "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx".
Ceci dit, une solution simple pour générer un mot de passe aléatoire peut être de faire :
Right(newid(), 12) => 12 caractères hexa-décimaux
Left(newid(), 8) => 8 caractères hexa-decimaux
Ou si vous être parano, vous pouvez toujour faire :
Replace(newid(), '-', '') (32 caractères hexa)
Pour tromper l'ennemi, on peut s'amuser à les regrouper ces caractères 2 par deux, et générer le caractère ASCII associé (en vérifiant que le caractère est bien saisissable au clavier ! )
Pour ce faire, on pourra créer ces deux fonctions (je n'ai pas trouvé de moyen de convertir une chaîne hexa en decimal) :
Note: On est obligé d'utiliser une procédure et non pas une fonction pour la génération du mot de passe car l'utilisation de newid() au sein d'une fonction est interdit (fonction non déterministique).
On peut ensuite utiliser la procédure comme suit :
Résultat :
Ca, c'est du mot de passe incrackable où je ne m'y connais pas !
Vous noterez que la liste des caractères retenus est en commentaire dans la procédure, vous pouvez changer les intervals afin de trouver un bon compris entre du tout alphanumérique et du tout pas bô comme ça
Bon, voilà, on a un générateur de mots de passe vraiment aléatoire (la demi-vie de la génération d'un GUID est d'environ... quelques centaines de milliers d'années, à compter qu'un PC de type Pentium 4 ne fait que ça pendant tout ce temps), donc ce serait bien domage d'obtenir des doublons ou des séquences qui se répètent !
Ceci dit, ça ne résoud pas le problème du genre "bon, j'ai mon select qui me retourne 25000 lignes, et je veux en prendre un au pif".
C'est là qu'intervient cet article trouvé chez Microsoft (et qui a motivé ce topic) :
http://msdn.microsoft.com/library/ [...] sp04c1.asp
Si vous y comprenez quelque chose, tant mieu pour vous, moi je panne rien
Donc, de mon côté, je reprends ma bonne technique utilisé par mon générateur de mot de passe :
Ouais, je sais, j'aurais pu faire les calculs au lieu de mettre des * 16 partout...
Et... Ne me demandez pas pourquoi le "99999999999999999999999999999999999999", je ne sais pas pourquoi
J'ai essayé "(16^31 * 5) - 1" comme la fonction est censée faire, mais ça ne marche pas, j'ai des valeurs > 1 (problèmes d'arrondis certainement, le FLOAT perdant énormément de précision)
Execution :
Résultats :
Voilà, j'espère que ça vous sera utile
Et tout cas, moi j'en avait besoin, donc j'ai pas perdu mon temps en passant 3 heures à écrire cet article, que j'espère vous le trouverez intéressant ^^
Edit:
La procédure stockée que j'ai indiqué, parceque c'est une procédure stockée et non une fonction ne resoud toujours pas la question "bon, j'ai mon select qui me retourne 25000 lignes, et je veux en prendre un au pif"... puisqu'on ne peut pas la faire intervenir dans la requête !
On pourra toujours écrire un curseur qui parcoure le résultat de la requête, compte le nombre de lignes, puis prend la (count * random)ième ligne. Vu le bordel que ça a été pour récupérer le nombre aléatoire, on n'est plus à ça prêt
Message édité par Arjuna le 10-06-2005 à 23:21:09