2

I have four tables: "users", "user_groups", "groups", "categories". "users" and "groups" are many_to_many relations through "user_groups". "groups" and "categories" are many_to_one relations.

I created the following SQL query, but I'm not sure how to implement it in Ruby on Rails:

SELECT u.*
FROM users u
WHERE EXISTS (SELECT 1
                FROM  user_groups ug,
                        groups g,
                        categories c
                WHERE u.id = ug.user_id
                AND   ug.group_id = g.id
                AND   g.category_id = c.id
                AND   c.id in ('1, 2, 3'))

What is the best way to implement it without using raw SQL in Ruby on Rails?

2 Answers 2

6

I am not a big fan of always translating complex queries into the ActiveRecord query language. Instead, I think it is perfectly fine to write complex queries in plain SQL because SQL is usually easier to write and to understand.

That said, I think this might work:

User.where(
  id: UserGroup.select('user_groups.user_id')
               .joins(groups: :categories)
               .where(categories: { id: [1, 2, 3] })
)
Sign up to request clarification or add additional context in comments.

6 Comments

I think it is perfectly fine to write complex queries in plain SQL because SQL is usually easier to write and to understand +1
I think so. @users = User.find_by_sql([ "SELECT u.* FROM users u WHERE EXISTS (SELECT 1 FROM user_groups ug, groups g, categories c where u.id = ug.user_id AND ug.group_id = g.id AND g.category_id = c.id AND c.id in ('1, 2, 3'))"])
“SQL is usually easier to write and to understand” — 💯
An ORM helps transform from a DBM-specific language to a more generic one, which anyone who's had to deal with recoding big corporate mainframes with proprietary language knows can be a major PITA. In those cases the ORM can save years of work. If translating between two DBMs that are SQL compliant, it's easier to make use of raw SQL when switching than it used to be, but still, sticking to the ORM will make it even easier; Imagine developing in SQLite or PgSQL on your laptop on a plane then switching to full Oracle on a big server with only a tiny change in the connection string. ORM FTW.
Fair enough, but isn't the question just a job security way of doing a left join? Or am I missing something?
|
0

This is probably a lot less complicated than you think:

class User
  has_many :user_groups
  has_many :groups, through: :user_groups
end

class Group
  has_many :user_groups
  has_many :users, through: :user_groups
  belongs_to :category
end

User.joins(:groups)
    .where(groups: { category_id: [1,2,3] })

ActiveRecord will handle joining the intermediate table.

Since this is a left inner join any rows without a match in the join table are omitted. No need to do WHERE EXISTS .... This will give you users that belong to at least one group with the category 1, 2 or 3.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.