2012-03-13 4 views
1

j'ai une relation de propriété entre deux tables, disent users(int user_id) et user_books(int user_book_id,int user_id,int book_id) et deux tables supplémentaires books(int book_id, varchar book_title, int author_id) et authors (int author_id, varchar author_name).Mysql joindre pour trouver des lignes « manquantes » « en rapport »

Étant donné un user_id spécifique, je veux obtenir les livres que l'utilisateur n'a pas ce que écrit par les auteurs qu'il a d'autres livres écrits par eux. Donc si l'utilisateur a BOOK1 (c'est-à-dire qu'il existe une ligne pour cela dans user_books) et n'a pas BOOK2 et BOOK3 écrit par le même auteur que BOOK1, je veux obtenir les ids pour BOOK2 et BOOK3.

Je suppose que je peux le faire en utilisant SELECT WHERE NOT IN() mais pour des raisons de performances, je recherche une solution basée sur les jointures.

+0

Avez-vous essayé d'utiliser une jointure externe? –

+0

comme je l'ai dit, je peux écrire cela en utilisant un "select where not in" mais je veux utiliser des jointures. Je suis conscient que cela devrait être fait en utilisant une jointure gauche mais je ne sais pas exactement comment. – epeleg

+0

J'ai aussi un problème avec le fait que certains utilisateurs peuvent déjà avoir plus d'un user_book, donc une jointure retournera l'identifiant de l'auteur plus d'une fois. – epeleg

Répondre

2

Je vérifie les performances par rapport à un « non » ou toute autre solution, mais je crois que ce qui suit fonctionnerait:

select exist.userId, b.bookTitle, a.authorName 
from (select distinct ub.userId, b.authorId 
     from userBooks ub 
      inner join books b on b.bookId = ub.bookId 
     where ub.userId = @userId) exist 
    inner join Authors a on a.authorId = exist.authorId 
    inner join Books b on b.authorId = a.authorId 
    left outer join userBooks ub on ub.bookId = b.bookId and ub.userId = exist.userId 
where ub.userId is null 

La table dérivée trouve tous les auteurs qu'un utilisateur aime le reste de la requête trouve d'autres livres par les mêmes auteurs

+2

vous avez raison - cela fonctionne, mais c'est beaucoup plus compliqué (comme prévu) que d'utiliser IN et NOT IN. Si vous faites une explication sur votre requête, vous voyez qu'il y a 4 requêtes PRIMARY et 2 requêtes DERIVED (une utilisant une table temporaire). Alors que "select * from books where author_id in (sélectionnez author_id de user_books comme ub joindre des livres comme b sur b.book_id = ub.book_id) et book_id not in (sélectionnez book_id de user_books où user_id = @uid);" a quatre requêtes, toutes utilisant where (et donc éligible pour l'accélération avec des index). Je suis d'accord que la mesure des deux approches est nécessaire pour choisir le meilleur. –

+0

@D Mac - exactement. Vous ne pouvez pas prendre une approche de gros "ne pas utiliser NON". Juste parce qu'il est possible de le faire sans eux ne signifie pas que vous devriez ... Beaucoup mieux que vous avez suggéré de regarder la façon dont la requête est traitée. Je voulais juste démontrer le possible mais je recommanderais basé sur l'analyse – kaj

+0

merci les deux, je vais essayer de travailler sur les choses à partir d'ici. @D Mac Dans la syntaxe que vous avez fournie, il manque un champ où user_id = @uid dans la première sous-requête, autant que je sache. et même difficile ma table de livres a un index sur author_id et sur book_id ni est utilisé. – epeleg

Questions connexes