2010-03-01 7 views
2

Je veux obtenir 1000 enregistrements d'une table au hasard, donc je l'utilise:Problème d'utilisation ROW_NUMBER() pour obtenir des enregistrements au hasard (SQL Server 2005)

SELECT top 1000 
     mycol1 
    , mycol2 
    , ROW_NUMBER() OVER (ORDER BY NEWID()) rn 
FROM mytable 

Cependant, je ne veux pas voir dans rn mon résultat, donc je fais:

SELECT mycol1 
    , mycol2 
FROM (
    SELECT top 1000 
      mycol1 
     , mycol2 
     , ROW_NUMBER() OVER (ORDER BY NEWID()) rn 
    FROM mytable 
) a 

Lorsque je fais cela, les résultats ne viennent plus au hasard. Ils viennent comme si je viens de dire top 10000 sans randomisation en utilisant row_number().

Lorsque je change la requête à

SELECT mycol1 
    , mycol2 
    , rn 
FROM (
    SELECT top 1000 
      mycol1 
     , mycol2 
     , ROW_NUMBER() OVER (ORDER BY NEWID()) rn 
    FROM mytable 
) a 

ils sont à nouveau au hasard.

Je suppose que le serveur SQL fait une sorte d'optimisation, en disant "hé, ce gars n'a pas besoin de la colonne rn de toute façon, alors ignorez-le". Mais cela entraîne un comportement inattendu dans ce cas. y-a-t-il un moyen d'éviter ça? PS: j'utilise l'astuce ROW_NUMBER() car mytable a 10 mio. lignes et

SELECT top 10000 * 
FROM mytable 
ORDER BY NEWID() 

s'exécute pour toujours, alors qu'avec ROW_NUMBER() cela ne prend que jusqu'à 30 secondes.

+1

A aimé votre "ROW_NUMBER() OVER (ORDER BY NEWID())". Avoir utilisé cela pour un grand effet à quelques reprises. –

Répondre

2

Vous pouvez également essayer d'utiliser le champ rn dans certains petits where comme

OÙ rn> 0 dans votre requête externe qui peut forcer le compilateur à mettre le champ RN par.

Aussi je pense que votre requête globale va être un problème si vous voulez échantillonner au hasard vos millions entiers d'enregistrements. Cela n'acceptera que le bloc d'enregistrements "premier disque" qui, bien qu'il ne soit pas garanti être le même, sera le même 10000.

Je suggère de créer un ensemble de 10 000 nombres aléatoires entre MIN (PrimaryKey) et le MAX (PrimaryKey) et puis en faisant WHERE PrimaryKey IN (...) ou similaire

+0

Merci d'avoir fait remarquer que cela n'attrapera qu'une partie de la table. Cependant, dans cet exemple, je peux vivre avec. – ercan

+0

Il pourrait ajouter WHERE rn <= 1000 pour obtenir le top 1000 et résoudre le problème –

1

Ajouter quelque chose comme Où rn est pas nul à la requête externe afin rn il est inclus dans le plan de requête et non optimisé dehors

0

Je luttais avec ce même problème. Je l'ai résolu avec CROSS APPLY et TOP. En gardant à l'esprit que CROSS APPLY tire ma table externe dans la portée de la table dérivée, je savais qu'il devait y avoir un moyen de le faire. Le code suivant entraîne l'ajout de 3 produits connexes aléatoires en fonction du fabricant.

INSERT INTO  ProductGroup (
        ParentId, 
        ChildId 
       ) 
SELECT   DISTINCT 
       P.ProductId, 
       CandidateInner.ChildId 
FROM   ProductRelated PR 
JOIN   Product P 
ON    PR.ChildId = P.ProductId 
CROSS APPLY  
       ( 
        SELECT  DISTINCT TOP 3 
           NewId() AS RandId, 
           Product.ManufacturerId, 
           ProductRelated.ChildId 
        FROM  ProductRelated 
        JOIN  Product 
        ON   Product.ProductId = ProductRelated.ChildId 
        WHERE  ManufacturerId IS NOT NULL 
        AND   Product.ManufacturerId = P.ManufacturerId 
        ORDER BY NewId() 
       ) CandidateInner 
LEFT JOIN  (
        SELECT  DISTINCT TOP 100 PERCENT 
           ParentId, 
           COUNT(DISTINCT ChildId) AS Ct 
        FROM  ProductGroup 
        GROUP BY ParentId 
        HAVING  COUNT(DISTINCT ChildId) >= 3 
       ) AlreadyGrouped 
ON    P.ProductId = AlreadyGrouped.ParentId 
WHERE   P.ProductId <> CandidateInner.ChildId 
AND    AlreadyGrouped.ParentId IS NULL 
ORDER BY  P.ProductId 

* Notez que cela insérera moins de 3 dans les 2 cas suivants:

1) En cas de < 3 produits connexes du fabricant 2) (Problématiques) Lorsque les 3 premiers au hasard renvoie la même produit à lui-même.

(1) ci-dessus est inévitable.

La façon dont j'ai géré (2) ci-dessus était de courir deux fois puis de supprimer les doublons. Ce n'est toujours pas 100%, mais statistiquement, c'est plus que suffisant pour mes besoins. Ceci est dans un script de nuit, mais j'aime toujours la rapidité d'avoir le <> en dehors de la CROSS APPLY - tout ce qui tire cette vérification entraîne des balayages des tables dérivées résultant de la jointure du fabricant, même si je la tire à l'intérieur signifiera que (2) n'est plus un problème, mais il est péniblement lent contre l'instant avec des index appropriés.

Questions connexes