I’m trying to do a multi-table join that has a NOT IN component. Tables are
Post -> Term Relationship -> Term
Post
has_many :term_relationships
has_many :terms, :through => :term_relationships
TermRelationship
belongs_to :post
belongs_to :term
Term
has_many :term_relationships
has_many :posts, :through => :term_relationships
The goal is to get all posts except for those in “featured” let’s say. My current query would looks like:
WpPost.includes(:terms).where("terms.term NOT IN (?)", ["featured"])
This works great if the only term that it has attached is “featured”. If the post belongs to “featured” and “awesome” it will still show because of “awesome”.
Anyway to exclude a row entirely? Will it require a subquery? And if it does, how would I go about doing that in rails?
Thanks all!
Justin
You misuse the
includes. It’s for eager loading, not for joining!But you’re right about the approach. It can be used in your case. But Rails won’t issue nested request for
NOT IN (?)even if it would be logical. You’ll get 2 queries instead (you’ll getNOT IN (id1, id2....,)instead ofNOT IN (SELECT ....)).So I would recommend you to use the squeel gem:
regular AR code (can also be prettified with squeel):
and then use the sqeel’s power:
(
inandnot_inare also aliased as>>and<<but I didn’t want to scary anybody)Note the using blocks and absence of symbols.
Some measurements based on Chinook Database under SQLite:
Relation with
joinsandlike:Here’s
NOT IN:FYI:
Conclusion: I don’t see the overhead caused by
NOT IN. 35.0 vs 37.5 isn’t noticeable difference. Few times 35.0 became 37.5 and vice verse.