Convert a SQL Query to Rails - ruby-on-rails

At the moment, I am doing my complex queries by hand so to speak. But I keep encountering problems. For instance.
query = "SELECT histories.candidate_id
FROM histories
WHERE histories.institution_id IN (?)
GROUP BY histories.candidate_id
HAVING COUNT(*)= ?"
cand = [Code.find_by_sql([query,
params['searches'][key],
params['searches'][key].size])]
class History < ActiveRecord::Base
belongs_to :candidate
end
create_table "histories", :force => true do |t|
t.string "job_title"
t.date "start_date"
t.date "finish_date"
t.string "basic_salary"
t.string "bonus"
t.integer "institution_id"
t.integer "candidate_id"
t.datetime "created_at"
t.datetime "updated_at"
end
class Candidate < ActiveRecord::Base
# has_and_belongs_to_many :codes
has_many :codes, :through => :CandidatesCodes
has_many :histories
has_many :contacts
has_many :compensations
end
This returns a list of candidate ids.. but want I want it to return is a list of candidates how would I do that the rails way?
This is brians suggestion, and I have tried this but I get uninitialized constant History::Candidates
cand = History.find(:all,
:joins => :candidates,
:select => "candidates.*",
:conditions => [ "institution_id IN (?)", params['searches'][key] ],
:group => [ "candidate_id HAVING count(*) = ?", params['searches'][key].size ]
)

Try this:
Candidate.all(
:joins => :histories,
:conditions => {:histories=> {:institution_id => params[:searches][key]}},
:group => "candidates.id",
:having => "count(candidates.id) >= %i" % params[:searches][key].size
)
This should generate the following SQL:
SELECT candidates.*
FROM candidates AS candidates
JOIN histories AS histories ON histories.candidate_id = candidates.id
WHERE histories.institution_id IN (1,2,3)
GROUP BY candidates.id
HAVING COUNT(candidates.id) >= 3

Give this a try (building on Dinesh's approach above:
candidates = History.find(:all,
:joins => :candidates,
:select => "candidates.*"
:conditions => ["institution_id IN (?)", params['searches'][key]],
:group => ["candidate_id HAVING count(*) = ?", params['searches'][key].size]
)
Warning - untested.

Try this out.
Assuming that you have mode of History
History.find(:all ,
:conditions=>[" institution_id IN (? ) ",params['searches'][key] ],
:group => ["candidate_id HAVING count(*) = ? " ,params['searches'][key].size ]
)

Related

Rails console : query to database

I have a model Report in which I defined those columns:
class Report < ActiveRecord::Base {
:id => :uuid,
:declarant_id => :uuid,
:reference => :integer,
:sent_at => :datetime,
:updated_by_id => :uuid,
:device_id => :string,
:created_at => :datetime,
:updated_at => :datetime,
:is_archived => :boolean,
:step_id => :uuid
}
and I want to filter the select query to get only the reports created 7 days ago, I defined this query related to some others inputs and methods :
r = Report.all.current.not_sent.only_finished_interventions.where("Report.created_at >= ?", 7.days.ago)
it not works !
when I do :
r = Report.all.current.not_sent.only_finished_interventions
I get the the requested reports.
can some one help to add the filter of 7.days.ago ?
I got this error in the rails c :
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: missing
FROM-clause entry for table "report" LINE 1: ... AND
"activities"."finished_at" IS NOT NULL) AND (Report.cre...
This should work:
.where("Reports.created_at >= ?", 7.days.ago)
Just make Report plural.
But I think Reports. is not necessary in your query bcs you don't join tables, so this should work too:
**`.where("created_at >= ?", 7.days.ago)`**

same foreign key used two times in a table

I have the followings tables
break_points
id: integer
break_point_name: string
schedules
id: integer
departure: int -> break_point_id
arrival:int -> break_point_id
departure_date:date
arrival_date: date
Both departure and arrival are breakpoints .
Then Will I to create an association * to * instead?
1.9.3-p547 :004 > s=Schedule.find(1)
Schedule Load (0.1ms) SELECT "schedules".* FROM "schedules" WHERE "schedules"."id" = ? LIMIT 1 [["id", 1]]
=> #<Schedule id: 1, departure_id: 1, departure_date: "2015-01-05", departure_time: 28800, arrival_id: 11, arrival_date: "2015-01-06", arrival_departure: 3600, bus_company_id: 1, created_at: "2014-11-08 22:55:00", updated_at: "2014-11-08 22:55:00">
1.9.3-p547 :005 > s.departure_break_points
BreakPoint Load (0.3ms) SELECT "break_points".* FROM "break_points" WHERE "break_points"."departure_id" = 1
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: break_points.departure_id: SELECT "break_points".* FROM "break_points" WHERE "break_points"."departure_id" = 1
from /home/fernando/.rvm/gems/ruby-1.9.3-p547#ticket_master/gems/sqlite3-1.3.10/lib/sqlite3/database.rb:91:in `initialize'
from /home/fernando/.rvm/gems/ruby-1.9.3-p547#ticket_master/gems/sqlite3-1.3.10/lib/sqlite3/database.rb:91:in `new'
from /home/fernando/.rvm/gems/ruby-1.9.3-p547#ticket_master/gems/sqlite3-1.3.10/lib/sqlite3/database.rb:91:in `prepare'
these are my migrations that generate the tables of the database
class CreateSchedules < ActiveRecord::Migration
def change
create_table :schedules do |t|
t.integer :departure_id
t.date :departure_date
t.time :departure_time
t.integer :arrival_id
t.date :arrival_date
t.time :arrival_departure
t.integer :bus_company_id
t.timestamps
end
end
end
class CreateBreakPoints < ActiveRecord::Migration
def change
create_table :break_points do |t|
t.string :city
t.integer :province_id
t.timestamps
end
end
end
and these are my models
These are my models
class BreakPoint < ActiveRecord::Base
attr_accessible :break_point_name, :city
belongs_to :province
end
class Schedule < ActiveRecord::Base
has_many :departure_break_points,class_name: "BreakPoint", :foreign_key => 'departure_id', :dependent => :destroy
has_many :arrival_break_points, class_name: "BreakPoint", :foreign_key => 'arrival_id', :dependent => :destroy
end
Add two relations in your model , but specify the foreign key
has_many :departure_break_points,class_name: "BreakPoint", :foreign_key => 'departure' :dependent => :destroy
has_many :arrival_break_points, class_name: "BreakPoint", :foreign_key => 'arrival', :dependent => :destroy

Rails 4: ActiveRecord => can't "select" has_one :through field from db

I am trying to retrieve one field from a "has_one :through" association. Assuming I have the following model:
class CreatePersons < ActiveRecord::Migration
def change
create_table :person do |t|
t.string :first_name
t.string :last_name
t.date :age
end
end
def change
create_table :location do |t|
t.decimal :longitude
t.decimal :latitude
t.string :city
t.string :state
t.references :person
end
end
def change
create_table :forecasts do |t|
t.timestamp :time
t.text :summary
t.decimal : precipitation
t.references :location
t.timestamps
end
end
end
... and the following model:
class Location < ActiveRecord::Base
belongs_to :person
belongs_to :forecast
end
class Forecast < ActiveRecord::Base
belongs_to :locaiton
has_one :person, :through => :location
end
class Person < ActiveRecord::Base
end
.. and I want to make query using ActiveRecord that pulls out ONLY the persons first name based on the forecast (i know its stupid, but this is just an excercise); so something like this:
# i realize this is a bad query, i'm putting this here to make my question stronger
Forecast.where("percipitation > ?", 1.5).select(:first_name)
or in SQL terms
select first_name from forecast fc
inner join locaiton loc on loc.id = fc.location_id
inner join person on person.id = loc.person_id
where precipitation > 1.5
SO I have tried something like this:
Forecast.joins(:person).where("percipitation > ?", 1.5).select(:person)
# Forecast.joins(:person).where("percipitation > ?", 1.5).select(:person).to_sql
# select 'person' from forecast fc
# inner join locaiton loc on loc.id = fc.location_id
# inner join person on person.id = loc.person_id
# where fc.percipitation > 1.5
this returns me empty instances of the Forecast object
so then I tried this:
Forecast.joins(:person).where("percipitation > ?", 1.5).select("person.first_name")
# Forecast.joins(:person).where("percipitation > ?", 1.5).select("person.first_name").to_sql
# select 'person.first_name' from forecast fc
# inner join locaiton loc on loc.id = fc.location_id
# inner join person on person.id = loc.person_id
# where fc.percipitation > 1.5
however, this also results in a colleciton of empty Forecast objects
HOWEVER, doing this results in exactly what i want, but this is already after the db has been queried:
result = Forecast.joins(:person).where("precipitation > ?", 1.5)
result.each do |forecast|
puts forecast.person.first_name # => "John", "Bob", "Jim"
end
Why can't I get just the first_name out of the DB using select and pull only first_name out of the database? I'm obviously missing something.
I don't know why your solution works at all.
Do it this way:
Forecast.where("precipitation > ?", 1.5).joins(:person).pluck("person.first_name")
It will return array of first names.
If you really need to use select (to get a scope):
Forecast.where("precipitation > ?", 1.5).joins(:person).select("person.first_name")

rails: using associated model's columns in :where

I'm not sure what's going on here. I have a scope I'm trying to create that works with my association:
class Subscription < ActiveRecord::Base
belongs_to :subscriber, :class_name => "User"
belongs_to :subscribable, :polymorphic => true
end
create_table :products do |t|
t.string :name
t.decimal :price
t.decimal :cost_per_unit
t.integer :user_id
end
create_table :subscriptions do |t|
t.string :name
t.decimal :price
t.decimal :cost_per_unit
t.integer :subscriber_id
t.integer :subscribable_id
t.string :subscribable_type
end
class Product < ActiveRecord::Base
has_many :subscriptions, :as => :subscribable, :dependent => :destroy
def self.lower_prices
Product.includes(:subscriptions).
where("products.price < subscriptions.price OR products.cost_per_unit < subscriptions.cost_per_unit" )
end
end
I'm trying to compare the lower price of the Product to the Subscription but this gives me the error:
ActiveRecord::StatementInvalid in Pages#subscribed_products
PGError: ERROR: missing FROM-clause entry for table "subscriptions"
LINE 1: ... WHERE (user_id != 2) AND (products.price < subscripti...
^
: SELECT COUNT(*) FROM "products" WHERE (user_id != 2) AND (products.price < subscriptions.price OR products.cost_per_unit < subscriptions.cost_per_unit)
What's wrong here?
The includes method doesn't do exactly what you think. Substitute joins for includes and it should Do What You Mean:
Product.joins(:subscriptions).
where("products.price < subscriptions.price OR products.cost_per_unit < subscriptions.cost_per_unit" )
or perhaps:
Product.includes(:subscriptions).joins(:subscriptions).
where("products.price < subscriptions.price OR products.cost_per_unit < subscriptions.cost_per_unit" )
joins translates to a JOIN in the resulting SQL query, so you can perform WHERE clauses on the joined table. include just asks Active Record to perform another query to select all the related records in the given table. If you do both together, Active Record creates a (rather long) all-in-one that both joins the two tables and uses the results to create both sets of objects.

Rails Many to Many with condition adding new condition

I am modelling users and movies with "liked" and "recommeded".
The models are as follows:
class Movie < ActiveRecord::Base
attr_accessible :title, :imbd_id
has_and_belongs_to_many :liked_by, :class_name => "User",
:conditions => { "type" => "like" }
has_and_belongs_to_many :recommended_by, :class_name => "User",
:conditions => { "type" => "recommend" }
end
class User < ActiveRecord::Base
has_and_belongs_to_many :movies_liked, :class_name => "Movie",
:conditions => { "type" => "like" }
has_and_belongs_to_many :movies_recommended, :class_name => "Movie",
:conditions => { "type" => "recommend" }
end
with one table to map the relationship:
create_table "movies_users", :id => false, :force => true do |t|
t.integer "user_id"
t.integer "movie_id"
t.string "type" # this can be "like" or "recommend"
t.string "status_id"
t.datetime "created_at"
t.datetime "updated_at"
end
Now I am getting an error when I try to do
u = User.first
m = Movie.first
u.movie_liked << m
with the following error
ruby-1.9.2-p290 :003 > u.movies_tweeted << m
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: movies.type: SELECT * FROM "movies" INNER JOIN "movies_users" ON "movies".id = "movies_users".movie_id WHERE ("movies_users".user_id = 1 AND ("movies"."type" = 'like'))
Any idea I can structure it nicely so I can just use the << operator and it will automatically assign the correct type?
Thanks!
You can create Recommendation and Like models and use them to join the Users with Movies. Or just a single model called don't know how(RecommendLike?), if you really wish to save on one table. And then:
User
has_many :recommendations
has_many :movies_recommended, :class_name => "Movie", :through => :recommendations
has_many :likes
has_many :movies_liked, :class_name => "Movie", :through => :likes

Resources