PG::Error: ERROR: column reference "status" is ambiguous in active_admin - ruby-on-rails

Using rails 3.2 with active_admin and seeing PG::Error: ERROR: column reference "status" is ambiguous when using a custom filter on active_admin in Rents.rb:
filter :travel_car_brand, as: :string
filter :travel_car_model, as: :string
The error points to:
: SELECT COUNT(DISTINCT "rents"."id") FROM "rents" LEFT OUTER JOIN "travels" ON "travels"."id" = "rents"."travel_id" LEFT OUTER JOIN "cars" ON "cars"."travel_id" = "travels"."id" WHERE ("cars"."brand" ILIKE '%mazda%') AND ("startDate" > '2014-08-04 10:15:14 +0200' and status = 'paid'):
it's interesting that the above has status = 'paid' since I'm not sure why its using that as a filter.
models
Rent.rb
belongs_to :travel
Travel.rb
has_one :car
and both rents table and travels table have a status attribute.

I've seen Lucas' answer but if this is a rails app, the SQL should be generated by the application, not hardcoded. Therefore changing the SQL directly is not the solution.
Instead, I would suggest you find the code that is adding the "paid" filter and modify it to declare the relevant model name.
Somewhere you probably have a scope:
scope :paid, where(status: 'paid')
change that to (for example):
scope :paid, where("model.status = 'paid'")

You need to chose wich table you want your attribute to select, or use both
e.g.
SELECT
COUNT(DISTINCT "rents"."id")
FROM "rents"
LEFT OUTER JOIN "travels" ON "travels"."id" = "rents"."travel_id"
LEFT OUTER JOIN "cars" ON "cars"."travel_id" = "travels"."id"
WHERE ("cars"."brand" ILIKE '%mazda%')
AND ("startDate" > '2014-08-04 10:15:14 +0200')
AND rents.status = 'paid'
or if you require both:
SELECT
COUNT(DISTINCT "rents"."id")
FROM "rents"
LEFT OUTER JOIN "travels" ON "travels"."id" = "rents"."travel_id"
LEFT OUTER JOIN "cars" ON "cars"."travel_id" = "travels"."id"
WHERE ("cars"."brand" ILIKE '%mazda%')
AND ("startDate" > '2014-08-04 10:15:14 +0200')
AND rents.status = 'paid'
AND travels.status = 'paid'

Your "status" column is ambiguous. Because SQL can't understand which one table's column you want. Rent.status or Travels.status that SQL can not understand.

Related

how to write a join condition in Ruby on Rails?

what I'm trying to do is to write something like the next query:
SELECT *
FROM Customers c
LEFT JOIN CustomerAccounts ca
ON ca.CustomerID = c.CustomerID
AND c.State = 'NY'
Notice that I'm not using any WHERE clause, but I need to my JOIN have a condition. I cannot make it work in Ruby on Rails.
Can you help me out?
You can join the tables with LEFT JOIN. Just pass the join condition in joins and you will get the expected result
Customer.joins("LEFT JOIN CustomerAccounts
ON CustomerAccounts.CustomerID = Customers.CustomerID
AND Customers.State = 'NY'")
#=> SELECT * FROM Customers LEFT JOIN CustomerAccounts ON CustomerAccounts.CustomerID = Customers.CustomerID AND Customers.State = 'NY'
Note: just .joins() does INNER JOIN so you need to specify the join with condition
Your SQL code, translated to activerecord, would look as follows (using joins):
Customer.where(state: 'NY').joins(:customer_accounts)
The code assumes, you have the association set up:
class Customer
has_many :customer_accounts
end

Respect negative conditions for advanced collection associations

I was trying to use this functionality introduced in #645 with conditional 2nd degree has_many ... through relationships with little success.
In my case:
a Course has_many :user_assigned_content_skills, -> { where(source: 'user') }, class_name: "ContentSkill"
and a ContentSkill belongs_to :skill and belongs_to :course
Then Course.ransack({user_assigned_content_skills_skill_name_not_cont: 'ruby'}).result.to_sql returns the following:
"SELECT courses.* FROM courses LEFT OUTER JOIN content_skills ON content_skills.course_id = courses.id AND content_skills.source = 'user' LEFT OUTER JOIN skills ON skills.id = content_skills.skill_id WHERE (skills.name NOT ILIKE '%ruby%')"
This means false positives again if a course has multiple content_skills. Any ideas how to retrieve all courses not being associated with a given skill name?
Many thanks for any insights!
You can get ids of courses associated with a given skill name, and then get a list of courses with ids that don't match the previous found. You can even make it as one composite SQL query.
Course.where.not(id: Course.ransack({user_assigned_content_skills_skill_name_cont: 'ruby'}).result)
This will generate an SQL like this:
SELECT courses.*
FROM courses
WHERE courses.id NOT IN (
SELECT courses.id FROM courses
LEFT OUTER JOIN content_skills ON content_skills.course_id = courses.id AND content_skills.source = 'user'
LEFT OUTER JOIN skills ON skills.id = content_skills.skill_id
WHERE (skills.name ILIKE '%ruby%')
)

Postgres error when trying to display all items through a relationship

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.

Specifying conditions on eager loaded associations returns ActiveRecord::RecordNotFound

The problem is that when a Restaurant does not have any MenuItems that match the condition, ActiveRecord says it can't find the Restaurant. Here's the relevant code:
class Restaurant < ActiveRecord::Base
has_many :menu_items, dependent: :destroy
has_many :meals, through: :menu_items
def self.with_meals_of_the_week
includes({menu_items: :meal}).where(:'menu_items.date' => Time.now.beginning_of_week..Time.now.end_of_week)
end
end
And the sql code generated:
Restaurant Load (0.0ms)←[0m ←[1mSELECT DISTINCT "restaurants".id FROM "restaurants"
LEFT OUTER JOIN "menu_items" ON "menu_items"."restaurant_id" = "restaurants"."id"
LEFT OUTER JOIN "meals" ON "meals"."id" = "menu_items"."meal_id" WHERE
"restaurants"."id" = ? AND ("menu_items"."date" BETWEEN '2012-10-14 23:00:00.000000'
AND '2012-10-21 22:59:59.999999') LIMIT 1←[0m [["id", "1"]]
However, according to this part of the Rails Guides, this shouldn't be happening:
Post.includes(:comments).where("comments.visible", true)
If, in the case of this includes query, there were no comments for any posts, all the posts would still be loaded.
The SQL generated is a correct translation of your query. But look at it,
just at the SQL level (i shortened it a bit):
SELECT *
FROM
"restaurants"
LEFT OUTER JOIN
"menu_items" ON "menu_items"."restaurant_id" = "restaurants"."id"
LEFT OUTER JOIN
"meals" ON "meals"."id" = "menu_items"."meal_id"
WHERE
"restaurants"."id" = ?
AND
("menu_items"."date" BETWEEN '2012-10-14' AND '2012-10-21')
the left outer joins do the work you expect them to do: restaurants
are combined with menu_items and meals; if there is no menu_item to
go with a restaurant, the restaurant is still kept in the result, with
all the missing pieces (menu_items.id, menu_items.date, ...) filled in with NULL
now look aht the second part of the where: the BETWEEN operator demands,
that menu_items.date is not null! and this
is where you filter out all the restaurants without meals.
so we need to change the query in a way that makes having null-dates ok.
going back to ruby, you can write:
def self.with_meals_of_the_week
includes({menu_items: :meal})
.where('menu_items.date is NULL or menu_items.date between ? and ?',
Time.now.beginning_of_week,
Time.now.end_of_week
)
end
The resulting SQL is now
.... WHERE (menu_items.date is NULL or menu_items.date between '2012-10-21' and '2012-10-28')
and the restaurants without meals stay in.
As it is said in Rails Guide, all Posts in your query will be returned only if you will not use "where" clause with "includes", cause using "where" clause generates OUTER JOIN request to DB with WHERE by right outer table so DB will return nothing.
Such implementation is very helpful when you need some objects (all, or some of them - using where by base model) and if there are related models just get all of them, but if not - ok just get list of base models.
On other hand if you trying to use conditions on including tables then in most cases you want to select objects only with this conditions it means you want to select Restaurants only which has meals_items.
So in your case, if you still want to use only 2 queries (and not N+1) I would probably do something like this:
class Restaurant < ActiveRecord::Base
has_many :menu_items, dependent: :destroy
has_many :meals, through: :menu_items
cattr_accessor :meals_of_the_week
def self.with_meals_of_the_week
restaurants = Restaurant.all
meals_of_the_week = {}
MenuItems.includes(:meal).where(date: Time.now.beginning_of_week..Time.now.end_of_week, restaurant_id => restaurants).each do |menu_item|
meals_of_the_week[menu_item.restaurant_id] = menu_item
end
restaurants.each { |r| r.meals_of_the_week = meals_of_the_week[r.id] }
restaurants
end
end
Update: Rails 4 will raise Deprecation warning when you simply try to do conditions on models
Sorry for possible typo.
I think there is some misunderstanding of this
If there was no where condition, this would generate the normal set of two queries.
If, in the case of this includes query, there were no comments for any
posts, all the posts would still be loaded. By using joins (an INNER
JOIN), the join conditions must match, otherwise no records will be
returned.
[from guides]
I think this statements doesn't refer to the example Post.includes(:comments).where("comments.visible", true)
but refer to one without where statement Post.includes(:comments)
So all work right! This is the way LEFT OUTER JOIN work.
So... you wrote: "If, in the case of this includes query, there were no comments for any posts, all the posts would still be loaded." Ok! But this is true ONLY when there is NO where clause! You missed the context of the phrase.

named_scope + average is causing the table to be specified more then once in the sql query run on postgresql

I have a named scopes like so...
named_scope :gender, lambda { |gender| { :joins => {:survey_session => :profile }, :conditions => { :survey_sessions => { :profiles => { :gender => gender } } } } }
and when I call it everything works fine.
I also have this average method I call...
Answer.average(:rating, :include => {:survey_session => :profile}, :group => "profiles.career")
which also works fine if I call it like that.
However if I were to call it like so...
Answer.gender('m').average(:rating, :include => {:survey_session => :profile}, :group => "profiles.career")
I get...
ActiveRecord::StatementInvalid: PGError: ERROR: table name "profiles" specified more than once
: SELECT avg("answers".rating) AS avg_rating, profiles.career AS profiles_career FROM "answers" LEFT OUTER JOIN "survey_sessions" survey_sessions_answers ON "survey_sessions_answers".id = "answers".survey_session_id LEFT OUTER JOIN "profiles" ON "profiles".id = "survey_sessions_answers".profile_id INNER JOIN "survey_sessions" ON "survey_sessions".id = "answers".survey_session_id INNER JOIN "profiles" ON "profiles".id = "survey_sessions".profile_id WHERE ("profiles"."gender" = E'm') GROUP BY profiles.career
Which is a little hard to read but says I'm including the table profiles twice.
If I were to just remove the include from average it works but it isn't really practical because average is actually being called inside a method which gets passed the scoped. So there is some times gender or average might get called with out each other and if either was missing the profile include it wouldn't work.
So either I need to know how to fix this apparent bug in Rails or figure out a way to know what scopes were applied to a ActiveRecord::NamedScope::Scope object so that I could check to see if they have been applied and if not add the include for average.
Looks like ActiveRecord is generating some bad SQL:
SELECT avg("answers".rating) AS avg_rating,
profiles.career AS profiles_career
FROM "answers"
LEFT OUTER JOIN "survey_sessions" survey_sessions_answers
ON "survey_sessions_answers".id = "answers".survey_session_id
LEFT OUTER JOIN "profiles"
ON "profiles".id = "survey_sessions_answers".profile_id
INNER JOIN "survey_sessions"
ON "survey_sessions".id = "answers".survey_session_id
INNER JOIN "profiles"
ON "profiles".id = "survey_sessions".profile_id
WHERE ("profiles"."gender" = E'm')
GROUP BY profiles.career
Presumably it's generated the left joins as part of getting the projected property, and the inner joins as part of getting the criteria: this wouldn't be invalid (just inefficient) if it assigned aliases to those tables, but it doesn't. Is there a way to specify an alias name from your app?

Resources