2010-03-26 9 views
1

J'ai cherché des moyens rapides pour sélectionner une ligne au hasard à partir d'une table et ont trouvé le site suivant: http://74.125.77.132/search?q=cache:http://jan.kneschke.de/projects/mysql/order-by-rand/&hl=en&strip=1SQL: sélectionnez la ligne aléatoire de la table où l'ID de la ligne n'est pas dans une autre table?

Ce que je veux faire est de choisir une URL aléatoire de ma table « » urls que je DON "J'ai dans mon autre table" urlinfo ". La requête que j'utilise sélectionne maintenant une url aléatoire depuis 'urls' mais j'ai besoin de la modifier pour retourner uniquement une URL aléatoire qui n'est pas dans la table 'urlinfo'.

Heres la requête:

SELECT url 
FROM urls JOIN (SELECT CEIL(RAND() * (SELECT MAX(urlid) 
            FROM urls 
            ) 
          ) AS urlid 
       ) AS r2 USING(urlid); 

Et les deux tables:

CREATE TABLE urls (
urlid INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
url VARCHAR(255) NOT NULL 
) ENGINE=INNODB; 


CREATE TABLE urlinfo (
urlid INT NOT NULL PRIMARY KEY, 
urlinfo VARCHAR(10000), 
FOREIGN KEY (urlid) REFERENCES urls (urlid) 
    ) ENGINE=INNODB; 

Répondre

3

Que diriez-vous de travailler à partir de cette solution au hasard:

SELECT TOP 1 * FROM urls 
WHERE (SELECT COUNT(*) FROM urlinfo WHERE urlid = urls.urlid) = 0 
ORDER BY NEWID() 
+0

-1 SQL ne dispose pas d'un opérateur ==; MySQL n'a pas de fonction NEWID(); et TOP 1 ne fonctionnera pas non plus dans MySQL :) – Andomar

+0

Mon mauvais sur l'opérateur ==, question d'origine jamais explicitement mentionnée mysql, j'ai peut-être manqué les références à InnoDB lors de mon premier examen de la question – LorenVS

1

Vous devez d'abord faire une jointure externe gauche join pour obtenir l'ensemble des enregistrements dans 'urls' qui ne sont pas dans 'urlinfo', puis choisissez un enregistrement aléatoire de cet ensemble.

SELECT * FROM urls 
LEFT OUTER JOIN urlinfo 
ON urls.urlid = urlinfo.urlid 
WHERE urlinfo.urlid IS null 

Choisissez maintenant une ligne aléatoire de cet ensemble - vous pouvez faire quelque chose comme

SELECT newUrls.url 
FROM ( 
     SELECT urls.urlid, urls.url FROM urls 
     LEFT OUTER JOIN urlinfo 
     ON urls.urlid = urlinfo.urlid 
     WHERE urlinfo.urlid IS null 
    ) as newUrls 
WHERE urls.urlid >= RAND() * (SELECT MAX(urlid) FROM urls) LIMIT 1 

Cependant, cela ne fonctionnera que si les urlids en urlinfo sont à peu près répartis au hasard dans l'ensemble des valeurs possibles.

+0

+1 Votre sous-requête a besoin d'un alias, et '*' donne une erreur de nom de colonne en double. Mais sinon belle réponse :) – Andomar

+0

Merci. Édité pour corriger les deux (j'espère - je n'ai pas de test mysql db à portée de main pour le moment) – David

+0

Get VMWare :) MySQL dit 'ERROR 1146 (42S02) à la ligne 20: La table 'newUrls' n'existe pas', et 'select urls.url' devrait être' newUrls.url' :) – Andomar

1

Vous pouvez utiliser where not exists pour exclure les lignes de l'autre table. Pour une ligne au hasard, une option est un order by rand() avec un limit 1:

SELECT url 
FROM urls 
WHERE NOT EXISTS (
    SELECT * 
    FROM urlinfo ui 
    WHERE ui.urlid = urls.urlid 
) 
ORDER BY RAND() 
LIMIT 1 
+0

"order by rand() limite 1" est la meilleure façon de choisir une ligne aléatoire si vous avez une petite table (<100 lignes) ou vous ne le faites pas se soucient de la performance. Si votre table est grande, cette requête pourrait générer 100 000 nombres aléatoires et les analyser tous pour le plus bas. C'est cher. – David

+0

Bien sur mon ordinateur, générer 100 000 nombres aléatoires et choisir le plus bas est plus rapide que d'exécuter 'SELECT MAX (urlid) FROM newUrls'. – Andomar