2010-09-07 3 views
3

Supposons que j'ai une table appelée examen. Chaque ligne contient:Sélectionnez les utilisateurs qui n'ont jamais réussi un examen

examDate
userId
passé

Ainsi, nous pourrions avoir les résultats suivants:

1 jan 2010, 7, faux
2 janvier 2010 , 8, vrai
3 jan 2010, 7, vrai
J an 3 2010, 9, false

Je veux exécuter un rapport qui inclut tous les utilisateurs qui n'ont JAMAIS passé l'examen. Dans l'exemple ci-dessus, seul userId 9 doit être renvoyé par cette requête.

Je ne veux pas faire quelque chose de vraiment inefficace comme:

Select * from exam where passed = 'false' and userId Not In 
(Select userId from exam where passed = 'true'); 
+2

nous ne sommes pas à faire des devoirs pour vous –

+1

Quelle base de données utilisez-vous? –

+2

C'est une vraie question, s'il vous plaît arrêtez de voter pour fermer parce que vous croyez que c'est "devoirs". –

Répondre

3

Vous voulez savoir deux faits de la même table: un utilisateur a échoué au test, et le même utilisateur n'a jamais passé le test. Donc, ce que vous voulez faire est de rejoindre la table à elle-même. Parce que l'un d'entre eux est un test de non-existence, cette jointure doit être une jointure null-left. Vous voulez dire sélectionner des lignes où aucune ligne ne correspond à la condition de jointure.

SELECT e0.* 
FROM exam AS e0 
LEFT JOIN exam AS e1 ON e1.userId=e0.userId AND e1.passed='true' 
WHERE e0.passed='false' 
AND e1.examDate IS NULL -- this can be any non-nullable column in e1 

Bien qu'il dépendra naturellement de votre schéma, SGBD et l'indexation, comme ce sont des jointures en effet souvent plus efficace qu'un sous-requête.

1

Essayez:

SELECT 
     ExamDate, UserId, Passed 
FROM 
     Exam e1 
LEFT JOIN 
     Exam e2 ON e1.UserId = e2.UserId AND e2.Passed = 'true' 
WHERE 
    e1.Passed = 'false' 
AND 
    e2.UserId IS NULL 

Sur une note de côté, je remarque que vous utilisez des caractères pour représenter une valeur true/false. Vous n'aurez peut-être aucun contrôle sur cela, mais il sera toujours conseillé d'utiliser le type de données booléen pour cette colonne, car SQL est beaucoup plus rapide pour comparer des valeurs booléennes à des valeurs textuelles. La requête devrait contenir: Passed = 0 ou 1 respectivement.

0

Voici ma solution, ça marche 100%!

SELECT 

    ex1.userId, 

    COUNT(ex2.userId) AS countPassed 

FROM 

    exam AS ex1 

LEFT OUTER JOIN exam AS ex2 

    ON ex2.userId = ex1.userId AND ex2.passed = 'true' 

GROUP BY ex1.userId 

HAVING countPassed = 0 
2

Dans un seul passage:

select userId 
from exam 
group by userId 
having max(passed) = 'false' 
Questions connexes