2008-10-02 12 views
3

J'essaie de sélectionner un échantillonnage aléatoire de 10% à partir d'une petite table. Je pensais que je venais d'utiliser la fonction RAND() et sélectionnez les lignes où le nombre aléatoire est inférieur à 0,10:Qu'est-ce que je fais de mal en utilisant RAND() dans MS SQL Server 2005?

SELECT * FROM SomeTable 
WHERE SomeColumn='SomeCondition' AND 
     RAND() < 0.10 

Mais je me suis vite découvert que RAND() retourne toujours le même numéro! Me rappelle de cette xkcd cartoon.

OK, pas de problème, la fonction RAND prend une valeur de départ. Je vais courir cette requête périodiquement, et je veux donner des résultats différents si je cours sur un autre jour, je sème avec une combinaison de la date et une ligne unique ID:

SELECT * FROM SomeTable 
WHERE SomeColumn='SomeCondition' AND 
     RAND(CAST(GETDATE) AS INTEGER) + RowID) < 0.10 

Je encore n'obtiens aucun résultat! Quand je montre les nombres aléatoires retournés par RAND, je découvre qu'ils sont tous dans une fourchette étroite. Il semble que l'obtention d'un nombre aléatoire à partir de RAND vous oblige à utiliser une graine aléatoire. Si j'avais une graine aléatoire en premier lieu, je n'aurais pas besoin d'un nombre aléatoire!

J'ai vu les discussions précédentes liées à ce problème:

SQL Server Random Sort
How to request a random row in SQL?

Ils ne me aident pas. TABLESAMPLE fonctionne au niveau de la page, ce qui est idéal pour une grande table mais pas pour une petite table, et il semble qu'elle s'applique avant la clause WHERE. TOP avec NEWID ne fonctionne pas car je ne sais pas à l'avance combien de lignes je veux.

Quelqu'un at-il une solution, ou au moins un indice?

Editer: Merci à AlexCuse pour un solution qui fonctionne pour mon cas particulier. Maintenant à la question plus large, comment faire RAND se comporter?

Répondre

6

Ce type d'approche (représenté par ΤΖΩΤΖΙΟΥ) ne garantit pas un échantillonnage de 10%. Il ne vous donnera que toutes les lignes où Rand() est évalué à < .10 qui ne sera pas cohérent.

Quelque chose comme

select top 10 percent * from MyTable order by NEWID() 

fera l'affaire.

éditer: il n'y a pas vraiment un bon moyen de faire se comporter RAND.Voilà ce que je l'ai utilisé dans le passé (bidouille alerte - il vous tue de ne pas pouvoir utiliser Rand() dans une UDF)

CREATE VIEW RandView AS 

SELECT RAND() AS Val 

GO 

CREATE FUNCTION RandomFloat() 
RETURNS FLOAT 
AS 
BEGIN 

RETURN (SELECT Val FROM RandView) 

END 

Il vous suffit select blah, dbo.RandomFloat() from table dans votre requête.

+0

Une approximation à 10% était assez bonne pour moi, mais votre réponse résout bien mon problème immédiat. J'aurais dû penser à vérifier une clause PERCENT sur TOP. –

2

Si votre table a une colonne (peut-être même la rowid colonne) qui est numérique au sens général, comme nombre entier, virgule flottante ou numérique SQL, s'il vous plaît essayez ce qui suit:

SELECT * FROM SomeTable WHERE SomeColumn='SomeCondition' AND 0*rowid+RAND() < 0.10 

Pour pour évaluer RAND() une fois pour chaque ligne, pas une fois au le début de votre requête.

L'optimiseur de requête est à blâmer. Peut-être existe-t-il un autre moyen, mais je crois que cela fonctionnera pour vous.

+1

Cette méthode ne fonctionne pas pour moi. Je n'ai que varchar et int, et je ne comprends pas pourquoi int aurait un comportement différent de numérique. –

+0

Si c'est l'optimiseur alors c'est très semblable au dessin animé! – Ken

+0

Peut-être que c'est ma faute, mais en disant "numérique" je voulais dire n'importe quel type numérique (entier, shortint, float, "SQL" numérique etc). Vérifiez ma réponse à nouveau, s'il vous plaît. – tzot

1

Cela semble fonctionner:

select * from SomeTable 
where rand(0*SomeTableID + cast(cast(newid() as binary(4)) as int)) <= 0.10 
0

Cela semble fonctionner

SELECT TOP 10 PERCENT * FROM schema.MyTable ORDER BY NEWID() 
Questions connexes