Can this events method be re-factored and simplified?
class Manager < User
has_and_belongs_to_many :customers
def events
Event.joins(:customers => :managers).where(:users => { :id => self }).select("DISTINCT(events.id), events.description, events.created_at")
end
end
I was hoping I could build the query on top of the Manager instance I currently have, but seem unable to do this. I tried the following, but got an error
def events
customers.joins(:events).select("DISTINCT(events.id), events.description, events.created_at")
end
and
current_user.events
But this results in MySQL error:
Mysql2::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DISTINCT(events.id), events.description, events.created_at FROM `customers` INNER' at line 1: SELECT `customers`.*, DISTINCT(events.id), events.description, events.created_at FROM `customers` INNER JOIN `customers_events` ON `customers_events`.`customer_id` = `customers`.`id` INNER JOIN `events` ON `events`.`id` = `customers_events`.`event_id` INNER JOIN `customers_managers` ON `customers`.`id` = `customers_managers`.`customer_id` WHERE `customers_managers`.`manager_id` = 27 ORDER BY created_at DESC
According to the MySQL manual, the DISTINCT keyword must come before the select_expr:
SELECT foo.*, DISTINCT(foo.id)
will give you that error.
SELECT distinct(foo.id), foo.*
will work. Try this:
customers.select("DISTINCT(events.id), events.description, events.created_at").joins(:events)
However, if all you're trying to do is to get the unique events per user (I assume that's what the DISTINCT is for), you could just use
current_user.events.group("events.id")
Related
I've got the following query that works:
jobs = current_location.jobs.includes(:customer).all.where(complete: complete)
However, when I add a where clause to query the first name of the customer table, I get an error.
jobs = current_location.jobs.includes(:customer).all.where(complete: complete).where("customers.fist_name = ?", "Bob")
Here is the error:
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "customers"
LINE 1: ...bs"."complete" = $2 AND "jobs"."status" = $3 AND (customers....
^
: SELECT "jobs".* FROM "jobs" INNER JOIN "jobs_users" ON "jobs"."id" = "jobs_users"."job_id" WHERE "jobs_users"."user_id" = $1 AND "jobs"."complete" = $2 AND "jobs"."status" = $3 AND (customers.last_name = 'Bob') ORDER BY "jobs"."start" DESC LIMIT $4 OFFSET $5
The current_location method:
def current_location
return current_user.locations.find_by(id: cookies[:current_location])
end
Location Model
has_many :jobs
has_and_belongs_to_many :customers
Job Model
belongs_to :location
belongs_to :customer
Customer Model
has_many :jobs
has_and_belongs_to_many :locations
How can I fix this issue?
includes will only join the table if you set a reference to the association.
When using includes you ensure a reference to the association in 2 fashions:
You can use the references method this will join the table whether or not there are any query conditions (If you MUST use raw SQL as shown in your question then this is the method you would need to use) e.g.
current_location.jobs
.includes(:customer)
.references(:customer)
Or you can use the hash finder version of where: (Please note that when using an associative reference in the where clause you must reference the table name, in this case customers and not the association name customer)
current_location.jobs
.includes(:customer)
.where(customers: {first_name: "Bob" })
Both of these will eager load the customer for the jobs referenced.
The first option (references) will OUTER JOIN the customers table so that all the jobs are loaded even if they have no customers as long as no query conditions reference the customers table.
The second option (using where) will OUTER JOIN the customers table but given the query parameter against the customers table it will act very much like an INNER JOIN.
If you only need to search the jobs based on customer information then joins is a better choice as this will create an INNER JOIN with the customers table but will not try to load any of the customer data in the query e.g.
current_location.jobs.joins(:customer).where(customers: {first_name: "Bob" })
joins will always include the associated table regardless of a reference in the query.
Sidenote: the all in both your queries is completely unnecessary
includes(:customer) does not necessarily join the customers table into the SQL query. You need to use joins(:customer) to force Rails to join the customers table into the SQL query and make it available to query conditions.
jobs = current_location.jobs
.joins(:customer)
.includes(:customer)
.where(complete: complete)
.where(customers: { first_name: 'Bob' })
I'm trying to use SQL to get information from a Postgres database using Rails.
This is what I've tried:
Select starts_at, ends_at, hours, employee.maxname, workorder.wonum from events where starts_at>'2018-03-14'
inner join employees on events.employee_id = employees.id
inner join workorders on events.workorder_id = workorders.id;
I get the following error:
ERROR: syntax error at or near "inner"
LINE 2: inner join employees on events.employee_id = employees.id
Sami's comment is correct, but since this question is tagged with ruby-on-rails you can try to use ActiveRecord's API to do the same:
Make sure that your models relations are defined
class Event < ActiveRecord::Base
belongs_to :employee
belongs_to :workorder
end
And then you can do something like:
Event
.where('starts_at > ?', '2018-03-14')
.joins(:employee, :workorder)
or
Event
.joins(:employee, :workorder)
.where('starts_at > ?', '2018-03-14')
And you don't need to worry which one goes first.
In general, it's suboptimal to create the SQL queries in rails if you don't absolutely need to because they're harder to maintain.
You request should look at this :
select starts_at, ends_at, hours, employee.maxname, workorder.wonum
from events
inner join employees on events.employee_id = employees.id
inner join workorders on events.workorder_id = workorders.id
where starts_at>'2018-03-14';
I've got a organization model and an organization_profile model. The organization_profile model has an approved column. I would like to be able to find all approved users by calling something like: Organization.approved. I found that the best way to handle this is probably through scope. However I can't seem to get it to work. This is what i am trying
scope :approved, -> {joins(:organization_profile).where('organization_profile.approved = ?', true) }
But then Organization.approved gives me all kinds of errors:
Organization Load (8.0ms) SELECT "organizations".* FROM "organizations" INNER JOIN "organization_profiles" ON "organization_profiles"."user_id" = "organizations"."id" WHERE (organization_profile.approved = 't')
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "organization_profile"
LINE 1: ...profiles"."user_id" = "organizations"."id" WHERE (organizati...
^
: SELECT "organizations".* FROM "organizations" INNER JOIN "organization_profiles" ON "organization_profiles"."user_id" = "organizations"."id" WHERE (organization_profile.approved = 't')
Can anyone tell me the correct code?
Your query is using organization_profile (singular) but your table name is organization_profiles (plural).
A slightly better way to do this (which also avoids using strings), is to turn the where clause into an Arel predicate (might not be the right word):
scope :approved, -> { joins(:organization_profile).where(OrganizationProfile.arel_table['approved'].eq(true)) }
The condition is SQL code that should reflect correctly the name of your tables which are always plural :
where('organization_profiles.approved = ?', true)
This works on SQLite3, but not on PostgreSQL.
The error I'm getting is PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
I'm trying to display all exercises that are in a group with the code: current_user.group.exercises
Here is the relationship
A group has_many workouts, and a workout has_many exercises
In my Group model I have has_many :exercises, through: :workouts
Any ideas?
EDIT 1:
Here is the SQL rails is generating:
SELECT DISTINCT "exercises".*
FROM "exercises"
INNER JOIN "workout_exercises" ON "exercises"."id" = "workout_exercises"."exercise_id"
INNER JOIN "workouts" ON "workout_exercises"."workout_id" = "workouts"."id"
INNER JOIN "groups_workouts" ON "workouts"."id" = "groups_workouts"."workout_id"
WHERE "groups_workouts"."group_id" = 2
ORDER BY exercise_order, workout_order
And here is the error:
PG::InvalidColumnReference: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
LINE 1: ..." WHERE "groups_workouts"."group_id" = 2 ORDER BY exercise_o...
^
: SELECT DISTINCT "exercises".* FROM "exercises" INNER JOIN "workout_exercises" ON "exercises"."id" = "workout_exercises"."exercise_id" INNER JOIN "workouts" ON "workout_exercises"."workout_id" = "workouts"."id" INNER JOIN "groups_workouts" ON "workouts"."id" = "groups_workouts"."workout_id" WHERE "groups_workouts"."group_id" = 2 ORDER BY exercise_order, workout_order
So this is a uniq constraint exception. On the model I had has_many :exercises, through: :workouts, uniq: true which Postgres didn't like.
To fix the error, I moved the uniq constraint from the model to the actual query. So in this situation, I just did current_user.group.exercises.uniq
This only sort of solves my problem. There are situations where I would want to have a uniq constraint at the model level, but I haven't been able to find a way to do that yet.
Each SQL variant has slightly different rules as to what expressions it accepts. For example, see Simulating MySQL's ORDER BY FIELD() in Postgresql and related links for information on this issue. If you give the specifics of the SQL you're generating, you can probably get more specific advice.
Let's say that I have 4 models which are related in the following ways:
Schedule has foreign key to Project
Schedule has foreign key to User
Project has foreign key to Client
In my Schedule#index view I want the most optimized SQL so that I can display links to the Schedule's associated Project, Client, and User. So, I should not pull all of the columns for the Project, Client, and User; only their IDs and Name.
If I were to manually write the SQL it might look like this:
select
s.id,
s.schedule_name,
s.schedule_type,
s.project_id,
p.name project_name,
p.client_id client_id,
c.name client_name,
s.user_id,
u.login user_login,
s.created_at,
s.updated_at,
s.data_count
from
Users u inner join
Clients c inner join
Schedules s inner join
Projects p
on p.id = s.project_id
on c.id = p.client_id
on u.id = s.user_id
order by
s.created_at desc
My question is: What would the ActiveRecord code look like to get Rails 3 to generate that SQL? For example, somthing like:
#schedules = Schedule. # ?
I already have the associations setup in the models (i.e. has_many / belongs_to).
I think this will build (or at least help) you get what you're looking for:
Schedule.select("schedules.id, schedules.schedule_name, projects.name as project_name").joins(:user, :project=>:client).order("schedules.created_at DESC")
should yield:
SELECT schedules.id, schedules.schedule_name, projects.name as project_name FROM `schedules` INNER JOIN `users` ON `users`.`id` = `schedules`.`user_id` INNER JOIN `projects` ON `projects`.`id` = `schedules`.`project_id` INNER JOIN `clients` ON `clients`.`id` = `projects`.`client_id`
The main problem I see in your approach is that you're looking for schedule objects but basing your initial "FROM" clause on "User" and your associations given are also on Schedule, so I built this solution based on the plain assumption that you want schedules!
I also didn't include all of your selects to save some typing, but you get the idea. You will simply have to add each one qualified with its full table name.