Rails query with group and count must include zeros - ruby-on-rails

I know this question has been asked before but I have read many of the posts and tried applying them to my situation and I just can't get it working. I'm a beginner who could use some help. Here are my models:
class Action < ActiveRecord::Base
belongs_to :Student
belongs_to :Meeting
belongs_to :ClassSection
end
class Meeting < ActiveRecord::Base
belongs_to :floorplan
has_many :actions
belongs_to :class_section
end
I am trying to get a count of actions for a single student across multiple meetings, including meetings where he/she had 0 actions. So, if there are two rows in the meetings table, ID=1 and ID=2, and student 83 has a single action with meeting_id=1 in the actions table, my query, which will include where(:student_id=>83) somewhere, should return something like
1=>1
2=>0
I hope this makes sense. I know the answer involves outer joins but I am bad at implementing them in pure SQL and worse at doing them through ActiveRecord. FYI I am using MYSQL. Thanks in advance for whatever help you can provide.

Meeting
.joins('LEFT JOIN actions ON meeting_id = meetings.id')
.where(student_id: 83)
.group('meetings.id')
.count('actions.id')
Explanation
.joins is the left/outer join that you intuited that you needed. It means "include at least one row for every meeting, even if there are no actions".
.group needs to be on the meeting id, since this will always be present and different meetings should be grouped separately.
.count needs to be on actions id. COUNT does not count null records, so meetings with no actions will be counted as 0.
It's a little bit weird that for a count of actions you nee to start your query with Meeting, but that is necessary when you want to include 0 counts. Otherwise there would be no way for SQL to know what meetings were missing!
For reference, the generated SQL is:
SELECT
COUNT(actions.id) AS count_actions_id,
meetings.id AS meetings_id
FROM "meetings" LEFT JOIN actions ON meeting_id = meetings.id
GROUP BY meetings.id

I think this should work fine just by grouping
Meeting.where(student_id: 83).group(:actions).count
This will return the hash you want
{1=>1, 2=>0}

Related

Looking for a way to track history in rails database

I'm considering this an add-on question of sorts to the thread below:
Using join tables in ruby on rails
So we have 'Student' and 'Course' scaffolds joined by a many-to-many association, but in addition there is also a 'Semester' scaffold and what I wish to do is, for each student that is added to a course, for the application to search for previous iterations of the same course through past semesters, to that it's known how many times a student has taken that class before. I'm kind of mixed up at the moment as to how to implement this, so I was hoping someone could help me pin down the logic and code I should be operating by.
Some underlying assumptions I have so far:
'Course' and 'Semester' should, like 'Student' and 'Course', be joined
by a many-to-many association (many courses are taught per semester,
and a course is taught for more than one semester).
There should be an action (let's say get_student) within the course
controller to locate the student via student_id. This would be the main area I'm scratching my head as to what to do. What would this method look like in code?
Within the student-course join table I should have an attribute
'attempts' which increments each time get_student finds this
student_id combined with the course_id that calls the method.This
would be the mechanism that actually tells how many times the course
had been attempted by the student.
I initially wondered if there should be a 'semester' controller
action to activate get_student across all semesters, but now I'm
thinking that get_student should work fine without that.
Appreciate any help I can get on this. Thanks.
This is not a good answer, just a comment.
I would comment, but hear will be more clear. I ll update for the other points. This is just an ongoing feedback/discussion, not an answer.
class Semester < ApplicationRecord
has_many :courses
end
class Course < ApplicationRecord
has_many :students
end
And
semester.courses[0].students => outputs the students array for that
This could be the method to calculate the number of student that did that course:
def studentForCourse
#input_params.course_id => course id you are selecting
semester = Semester.find(input_params)
semester.courses.each do |course|
if course.id = input_params.course_id
nstudents = course.students.size
end
end

Retrieve teachers from User and Lesson models: an efficient way

I have two models: User and Lesson.
User has_many :lessons
Lesson belongs_to :user
I have to retrieve User who are teachers (they have at least one lesson). Which is an efficient way?
For now I used a where condition that is traslated in a "IN" in Sql, so slow, or n query where n is the number of lessons. Thank you :)
If you want teachers, and teachers are users that have at least one lesson, then do this:
User.joins(:lessons)
That will perform an inner join thus excluding users that don't have any lessons.
But since a user can have multiple lessons, the returned table will potentially have multiple users. One solution for this could be to group by the users' ids.
User.joins(:lessons).group("users.id")
Or call distinct
User.joins(:lessons).distinct

How to use raw sql to extract values from a JOIN TABLE in Rails

I've two tables users and places, joined by places_users.
I want to retrieve the last 5 places_users.
I'm trying:
Place.select("* from places_users")
but that's generating:
SELECT * from places_users FROM "places"
Do I need to use ActiveRecord directly? Or something else...
Using instances of the models would not be appropriate, as that will give me just a users' places, or all the users that have selected a place.
Thanks for your help in advance.
you could try something like:
PlaceUser.includes(:place, :user).last(10)
which should give last 10 records from PlaceUser table with related places and users
if you don't have joined table you could create it:
class PlaceUser < ActiveRecord::Base
belongs_to :user
belongs_to :place
end

Rails - count query while joining three tables to one

I have a fairly straightforward query that I can't seem to get right....
My model:
User - has many Friendships (with other users)
User - has many submissions
User - has many comments
User - has many votes
I need a count that represents
All current_user's friends, whose submissions, comments or votes created_at dates are > current_user.last_refresh_date
Right now, I am building up an array by iterating over friendships and adding all submissions, comment and votes. I then re-iterate this built-up array while comparing the dates to determine if the count should be incremented. Not the most ideal solution.
Edit:
#TobiasCohen
Efficient solution. Thanks!
Followup:
I wish to add yet one more count to the present query. I need to count all new comments & votes on the current_user.submissions that are not part of the original count (ie. not a friend).
Psuedo-code :
current_user.submissions.join(:comments, :votes, :friends).where('last_activity >? AND friend_id != ?', current_user.last_refresh_date, current_user.id).count
I can't quite get the query correct (new to complex queries via active record).
I was going to make it a separate query and then add it to the original count. Can it be absorbed into one query instead of two?
I think you'd get the best results by adding a cache column on User, let's call it :last_activity_at, then update this with an after_create callback on Submission, Comment and Vote.
class Submission < ActiveRecord::Base
belongs_to :user
after_create :update_user_last_activity_at
private
def update_user_last_activity_at
user.update_attribute :last_activity_at, Time.now
end
end
You could then fetch users simply with:
current_user.friends.where('last_activity_at > ?', current_user.last_refresh_date)

Rails: Relationship between two loosely related models

I am working on a Ruby on Rails 3 web application and am not sure how to relate two of the models.
In our organization sales reps go out on appointments. If the appointment is successful, it will result in creating an order (which then has the items ordered related to it, but that's for another day.) If this appointment is not successful, it will be marked as no sale and as you might have guessed, no order is created.
On the other hand, sometimes sales happen without an appointment. For example, a customer may call into the store and order something. In this case, an order can exist without an appointment.
It would be simple if there were no relationship between orders and appointments, but there has to be for ease of use for the end user. For example, if an appointment generates an order, but later the buyer cancels, they will mark the appointment as sale cancelled and then the system should automatically set the order as cancelled. Likewise,they may choose to cancel the order, then the appointment would have to be cancelled automatically by the system.
How does a developer handle something like this? Does the appointment :have_many => orders? does the order :belong_to => appointments? I don't know what to do!
Please help me with this, I am a pretty new rails developer and I feel in over my head! Thank you!
As you already said, the following will work fine:
class Appointment < ActiveRecord::Base
has_many :orders
end
class Order < ActiveRecord::Base
belongs_to :appointment
end
belongs_to requires the field appointment_id to be present in the orders table. But, if the order is not associated with an order then appointment_id does not need to be set. You can have multiple belongs_to associations for a given class.

Resources