2009-09-16 4 views
0

Un de mes collègues a découvert un comportement dans SQL Server que je ne connaissais pas.Est-ce une bonne ou une mauvaise façon de générer des nombres aléatoires pour chaque enregistrement?

CREATE VIEW dbo.vRandNumber AS 
SELECT RAND() as RandNumber 
GO 

CREATE FUNCTION dbo.RandNumber() RETURNS float AS 
RETURN (SELECT RandNumber FROM vRandNumber) 
GO 

DECLARE @mytable TABLE (id INT) 
INSERT INTO @mytable SELECT 1 
INSERT INTO @mytable SELECT 2 
INSERT INTO @mytable SELECT 3 

SELECT *, dbo.RandNumber() FROM @mytable 

Ce semble être le moyen le plus rapide de générer une valeur « aléatoire » pour chaque enregistrement dans un ensemble de données. Mais je ne suis pas complètement sûr si c'est le résultat d'un comportement documenté, ou en profitant d'une convergence de coïncidences bizarre.

Est-ce que utiliser quelque chose comme ça?


EDIT

Ce n'est pas une question sur le bien-fondé de la fonction RAND() elle-même, mais l'utilisation de la combinaison UDF/VIEW pour forcer à recalcule sur chaque ligne . (Utiliser simplement RAND() dans la requête finale, au lieu de dbo.RandNumber(), donnerait la même valeur pour chaque enregistrement.)

De plus, le but est que la valeur soit différente chaque fois que vous le regardez . Permettant ainsi la sélection aléatoire d'enregistrements, par exemple.

EDIT

Pour SQL Server 2000+.

Répondre

3

Je ne ferais pas cela pour un morceau de logiciel que je voulais continuer à travailler sur les futures versions de SQL Server. J'ai trouvé un moyen de renvoyer des valeurs différentes de RAND() pour chaque ligne dans une instruction select. Cette découverte était 1) un peu un hack et 2) a été faite sur SQL Server 2005. Elle ne fonctionne plus sur SQL Server 2008. Cette expérience me donne plus de chance de me fier à la supercherie pour que rand() retourne une valeur aléatoire par rangée.

En outre, je crois que SQL Server est autorisé à optimiser les appels multiples à un UDF ... bien que cela puisse changer car ils permettent certaines fonctions non déterministes maintenant. Pour SQL Server 2005 uniquement, un moyen de forcer rand() à s'exécuter par ligne dans une instruction select. ne fonctionne pas sur SQL Server 2008. Non testé sur une version antérieure à 2005:

create table #t (i int) 
insert into #t values (1) 
insert into #t values (2) 
insert into #t values (3) 

select i, case when i = 1 then rand() else rand() end as r 
from #t 

1 0.84923391682467 
2 0.0482397143838935 
3 0.939738172108974 

Aussi, je sais que vous avez dit que vous ne demandez pas sur le caractère aléatoire de rand(), mais je une bonne référence est: http://msdn.microsoft.com/en-us/library/aa175776(SQL.80).aspx. Il compare rand() à newid() et rand (FunctionOf (PK, datetime actuel)).

+0

CHECKSUM (NEWID()) fonctionne au moins sur SQL 2000+. Cela dépend d'un certain comportement qui peut être supprimé dans un correctif SQL 2005 – gbn

0

Si je devais choisir un nombre aléatoire pour chaque ligne de SQL, et vous pourriez me prouver que RAND() est la génération de nombres aléatoires vrai ...

Oui. J'utiliserais probablement quelque chose comme ça.

1

Cela dépend de ce pour quoi vous avez besoin de la valeur aléatoire. Cela dépend aussi du format que vous avez besoin de la valeur dans INTEGER, VARCHAR, etc.

si je dois trier les lignes au hasard, je fais quelque chose comme

SELECT * 
FROM [MyTable] 
ORDER BY newID() 

De même, vous pouvez générer une table de ints en utilisant l'identité "fonctionnalité" de SQL Server et effectuer une requête similaire et qui pourrait vous donner un nombre aléatoire.

Mon collègue avait besoin d'un entier aléatoire par ligne, il a donc ajouté un champ calculé à notre table et cela génère un nombre aléatoire (entier) par ligne retournée dans une requête. Je ne suis pas sûr que je recommande ceci; il a causé des problèmes dans certains outils mais il a donné des nombres entiers aléatoires pour chaque table. Nous pourrions alors combiner ma solution de newid() et cette table et obtenir un ensemble de nombres aléatoires si nécessaire.

Je reviens donc à cela dépend. Pouvez-vous élaborer sur ce dont vous avez besoin?

Mise à jour: Voici la définition de table snippet mon collègue l'habitude d'avoir une colonne calculée renvoie un nombre aléatoire différent par ligne, chaque fois que la table est interrogée:

CREATE TABLE [dbo].[Table](
    -- ... 
    [OrderID] [smallint] NOT NULL, --Not sure what happens if this is null 
    -- ... 
    [RandomizeID] AS (convert(int,(1000 * rand(([OrderID] * 100 * datepart(millisecond,getdate())))))), 
    -- ... 
) 
+0

À l'heure actuelle, il est tout à fait académique, juste un cas d'obtenir des lignes à partir d'un jeu d'enregistrements de façon aléatoire . En ce que différents enregistrements sont nécessaires à chaque fois. Peut-être pondéré, mais en utilisant [weight] * dbo.RandNumber() donne cela. Donc, simplement, un moyen d'obtenir une ligne générée aléatoirement pour chaque enregistrement, ce qui est différent chaque fois que vous interrogez la table. – MatBailie

+0

Je n'ai pas spécifié sql-2000 compatible, mais aussi (afaik) newID() ne renvoie pas un nombre aléatoire en tant que tel. Ce n'est ni un nombre (utilisé pour multiplier un poids, par exemple) pas vraiment aléatoire car il est basé sur le temps, le matériel, etc. Mais alors, je ne sais pas si c'est moins aléatoire que le RAND() fonction. – MatBailie

+0

Quel est le calcul que votre collègue met dans le champ calculé? J'ai juste essayé d'utiliser RAND() et ai obtenu une valeur différente à chaque exécution, mais la même valeur pour chaque enregistrement ... – MatBailie

0

Je ne voudrais pas utiliser cette .Pour autant que je sache, RAND() utilise l'heure du système comme une graine et produit les mêmes valeurs lorsqu'elle est exécutée plus d'une fois rapidement les unes après les autres. Par exemple, essayez ceci:

SELECT *, 
      RAND() 
FROM  SomeTable 

RAND() vous donnera la même valeur pour chaque ligne.

+1

Ce comportement n'est pas dû à la proximité des temps. C'est parce que RAND() est exécuté une seule fois et pas une fois par enregistrement. Cet exemple utilise également RAND() mais l'obscurcit derrière un UDF et un VIEW. Le forçant ainsi à recalculer à chaque fois. Dans mon exemple, les trois enregistrements ont des valeurs différentes à chaque fois. Comme ils sont au hasard, je ne suis pas sûr. Mais ils ne vont certainement pas être les mêmes (sauf par hasard). – MatBailie

+0

Ma requête ne concerne pas vraiment le mérite de RAND(), mais le mérite d'utiliser le combo UDF/VIEW pour forcer un reclaculation pour chaque ligne. – MatBailie

+1

Oh, je vois. Merci pour les informations sur RAND() s'exécutant une seule fois par ensemble d'enregistrements, je ne le savais pas. Aussi, désolé d'avoir mal compris votre question. –

0

L'approche vue et udf est maladroite pour moi: excès d'objets triviaux pour utiliser une fonction imparfaite.

J'utilise CHECKSUM(NEWID()) pour générer un nombre aléatoire (plutôt que RAND() * xxx), ou le nouveau SQL Server 2008 CRYPT_GEN_RANDOM

+0

NEWID() ne résoudrait-il pas toujours en constanst de la même manière que RAND()? Donc, la combinaison vue/udf est toujours nécessaire? (C'est la combinaison vue/udf qui est intrinsèquement en question, permettant de réévaluer ce qui serait normalement considéré comme une expression constante pour chaque enregistrement.) – MatBailie

+0

NEWID() est * par appel *, et non par instruction. Donc, ce sera différent par rangée. – gbn

+0

Réponse commune de la mienne: http://stackoverflow.com/search?q = newid + RAND + utilisateur% 3A27535 – gbn

Questions connexes