J'ai une base de données d'environ 8 millions de lignes, dont je veux sélectionner au hasard n lignes. Tout d'abord, j'ai lu la question populaire et similaire ici sur Stackoverflow et l'article sur MSDN , mais je pense que le Les réponses ne conviennent toujours pas à mes besoins.
Les solutions proposées fonctionnent bien si je veux un certain pourcentage de lignes sélectionnées au hasard sans conditions supplémentaires. Mais ce que je veux sélectionner N lignes au hasard (par exemple au plus 5 rangées), tous correspondant à une certaine condition. P>
Ma base de données contient des mots avec des informations telles que leur partie de la parole, de la balise, du lemme et du jeton. Maintenant, je veux effectuer une requête pour sélectionner 5 mots aléatoires tous similaires au mot dans la requête (par exemple, donnez-moi 5 mots similaires à flou em>), ceci est déterminé en regardant uniquement des mots avec la même partie de la parole et une valeur de la distance de Levenshtein au-dessus d'un certain seuil. J'ai une fonction dans SQL Server capable de calculer la distance de Levenshtein. P> Le problème avec les méthodes susmentionnées est qu'ils doivent soit exécuter sur tous les enregistrements et calculer la distance de Levenshtein (qui prend beaucoup de temps!) Ou ne m'oposposent que pour sélectionner un pourcentage au lieu de n lignes. p> une requête qui fonctionne suffisamment bien est la suivante: p> Cependant, avec Seul le top je reçois toujours les mêmes résultats. J'ai besoin de mon sommet pour être aléatoire. Les méthodes telles que l'utilisation de NewID () vont d'abord aller sur toute la base de données, puis sélectionner au hasard, ce qui n'est pas mon comportement voulu car ils prennent beaucoup trop longtemps. P> Est-ce que quelqu'un a une idée de sélectionner NE SELECT sur NE une énorme base de données? p> EDIT: strong> p> Quelqu'un (pas sur Stackoverflow) peut m'avoir proposé une solution avec l'option EM> Clause et le mot clé rapide em>, qui récupère le premier nombre N nombre de rangées qu'il trouve. P> Avec l'option (Fast 5), je reçois la meilleure performance jusqu'à présent (10 secondes sur une table de 8 millions de rangée). J'ai également changé la fonction Levenshtein à partir d'une implémentation SQL en une implémentation de la bibliothèque écrite C # qui a considérablement augmenté de manière considérable. P>
Select top 5 * from (
SELECT DISTINCT lower(Words.TOKEN) as LTOKEN, Words.LEMMA, TagSet.POS_Simplified, TagSet.TAG
FROM Words JOIN TagSet on Words.TAG = TagSet.TAG
WHERE NOT Words.LEMMA = 'monarchie' AND TagSet.POS_Simplified = 'noun'
AND TagSet.TAG = 'NOM' AND NOT Words.TOKEN = 'monarchie'
AND [dbo].clrEditDistance('monarchie', Words.Token) > 0.5
) AS T
ORDER BY NEWID()
OPTION(fast 5)
4 Réponses :
Dans l'ordre, allez obtenir des données aléatoires, vous devez passer par toutes les lignes correspondant à votre clause WHERE. La recherche ne sera effectuée que sur les rangées qui correspondent à votre expression pour que ce ne soit pas la table de la table complète. Si vous avez beaucoup d'enregistrements correspondant à votre recherche, vous pouvez faire quelque chose comme: mais bien sûr, cela ne sera pas vraiment aléatoire. p> p>
C'est une solution qui fonctionne, mais je cherche quelque chose de vraiment aléatoire. Bien que je réalise que cela puisse affecter les performances sage.
De votre question, je suppose que vous savez que de nombreuses lignes correspondront à votre edit_distance> 0.5 code> condition. Mais SQL Server ne sait pas cela. Un moyen de partager cette infromation avec SQL Server consiste à écrire une requête plus explicite à l'aide de variables de table.
declare @results table (id bigint, name varchar(100))
while (select count(*) from @results) < 5
begin
insert @results
(name)
select name
from (
select top 100 *
from dbo.Words
order by
newid()
) as SubQueryAlias
where dbo.edit_distance(left(name,4), 'APRS', 100) < 3
end
select top 5 *
from @results
Je dois admettre que je ne suis pas expérimenté dans SQL Server et je ne suis pas connu avec la façon dont vous êtes venu à votre solution. Pourquoi la deuxième instruction SELECT sélectionne-t-elle également un nom?
La deuxième requête SELECT fournit de nouvelles lignes à être insérées dans @Results code>. Il doit correspondre à la liste d'insertions. Dans mon exemple, la liste d'insertions est
(nom) code>, de sorte que la requête sélectionne vient de sélectionner
nom code>.
Je pense qu'il y a une limite fondamentale sur la rapidité avec laquelle vous pouvez faire ce que vous recherchez. Si vous souhaitez choisir des enregistrements de la table rapidement, vous avez besoin d'un moyen d'utiliser un index. Disons que vous ayez un identifiant de colonne entier séquentiel et c'est l'index en cluster: vous pouvez sélectionner des enregistrements avec des valeurs d'identification aléatoires distinctes que vous générez, mais vous n'avez aucune garantie que chaque identifiant entre min (ID) et max (ID) vous donne une rangée dans la table pour que vous puissiez vous retrouver avec moins de lignes que vous avez demandé. P>
Une chose que vous pourriez faire est de prendre la requête qui a les conditions que vous souhaitez appliquer et ajoutez une norme Row_Number (voir Vous pouvez gérer des "trous" en identifiant en écrivant une fonction de valorisation de la table qui utilise une boucle (continuez simplement à sélectionner des valeurs aléatoires jusqu'à ce que vous obteniez le nombre de résultats que vous souhaitez) mais qui est inélégant et pourrait avoir des problèmes de concurrence en fonction de la Modèles d'accès sur votre base de données. Donc, cela dépend de vos besoins dans ces salaires. P>
Éviter une analyse complète va être difficile. Si vous aviez une colonne que vous pourriez sélectionner facilement facilement, par exemple, vous avez eu une colonne d'identité "dense" avec peu de lacunes, remplacez l'approche de Klark avec la modification suivante:
declare @results table (id bigint, name varchar(100)) while (select count(*) from @results) < 5 begin insert @results (name) select name from ( select * from dbo.Words WHERE IDCOLUMN = CONVERT(INT,RAND()) * APPX_NUMBER_OF_ROWS ) as SubQueryAlias where dbo.edit_distance(left(name,4), 'APRS', 100) < 3 end select * from @results)
Ajouter à la fin de votre requête: Commandez par NewID () Trière de tri dernier dans la requête exécutée. Ou essayez cette requête: Sélectionnez * à partir de () comme étant Ordonnez par NewID ()
Outre l'aspect aléatoire de votre question, il n'est-il pas préférable d'utiliser les capacités complexes intégrées (contenant, etc.) de SQLServer?
@ realnumber3012 Votre première suggestion passera toujours sur tous mes dossiers et il faut environ 5 minutes à compléter (ce qui est fondamentalement la même performance lorsque je voudrais exécuter la requête pour chaque enregistrement au lieu d'un peu nombreux). Votre deuxième suggestion ne fera que randomiser les 5 rangs résultants de ma requête qui n'est pas ce que je veux. Je veux 5 rangées aléatoires de la table, pas un ordre aléatoire des 5 premières lignes.
@Davek Pour être honnête, je n'ai pas réalisé les capacités complètes de la fonction contient SQLServer. Je ne sais pas si cela correspondra à mes besoins, mais je vais certainement examiner.
Top n order_by nouvelleID () ne va pas seulement randomiser l'ordre du top n; Cela attribue implicitement une nouvelle ligne à chaque rangée et, étant donné que NeufID est bien distribuée, cela vous donnera des ensembles aléatoires (puits, pseudo-pseudorandom) de n rangées hors de votre sous-requête. Je m'inquiète de cette "option (rapide 5)" du point de vue d'une aléatoire; Il me semble que permettre à SQL Server choisir un ordre pratique pour éviter que le pseudorandom commande à cet ordre de Newid () vous donne.
En fait, des neufids sont uniques, cela vous donne un "pseudorandom sans remplacement". Voir Discussion aléatoire VS Shuffle