Query Between Two Tables Using Ruby - mysql

My code is this
contact = UserContact.find(:all,:select=>"distinct app_id,number",:conditions=>"number ='1234'")
arr=[]
contact.each do|c|
arr << c.app_id
end
name=User.find(:all,:conditions=>"id in(#{arr.join(',')}")
I takes two much time Can i do this using join
Thanks

You should do smth like this
User.find(:all, :joins => :user_contacts, :conditions => "user_contacts.number = '1234'")
user should have association. In user.rb should be:
has_many :user_contacts, :foreign_key => :app_id
But it is bad style to name column "app_id" it should be "user_id"(convention over configuration). Renamed it. After rename you can remove ", :foreign_key => :app_id"

Unfortunately you can NOT do "includes" and "select" in the same Active record Query. So this will not work...
contacts = UserContact.includes(:users).where("number = ?", '1234').select(:app_id, :number).uniq
BUT.
FIRST: app_id looks like it should be called "user_id"
SECOND: What version of Rails are you using? You might want to use the "pluck" method if you are using rails 3.2. Hence
user_ids = UserContact.where("number = ?", '1234').pluck(:app_id).uniq
users = User.where(:id => user_ids)
THIRD: in ruby instead of doing this:
arr=[]
contact.each do|c|
arr << c.app_id
end
do this:
arr = contact.inject([]) {|arr, c| arr << c.app_id}
FOURTH: As in all my examples your syntax is mostly Rails 2. I assume you are using rails 2? If so you might need to upgrade.
FIFTH: use plural variable names if you return more than one object. Hence
contact = UserContact.....
name=User.....
should be
contacts = UserContact.find.....
users = User.find.....
LAST:
User.joins(:user_contacts).where("user_contacts.number = ?", '1234')
might be good

Related

convert mysql query into rails query

Hi all I have a problem converting mysql query into rails query.
I have these models -
class User < ApplicationRecord
has_many :comments, foreign_key: "commenter_id"
end
class Comment < ApplicationRecord
belongs_to :commenter, class_name: "User"
end
Can anyone help me out with converting following query into rails query-
UPDATE comments
INNER JOIN users on comments.commenter_id = users.id
SET comments.deleted_at = users.deleted_at
WHERE users.deleted_at IS NOT NULL
I am trying to make soft-delete comments whose commenter was softly deleted.
UPDATE 1:
so far I can able to do it by using this-
User.only_deleted.includes(:comments).find_each do |u|
u.comments.update_all(deleted_at: u.deleted_at)
end
But I want to do this on single query without having to iterate over the result.
UPDATE 2:
I am using acts_as_paranoid gem, so unscoping user is needed and my final query became:
User.unscoped{Comment.joins(:commenter).where.not(users: {deleted_at: nil}).update_all("comments.deleted_at = users.deleted_at")
This should work on MySQL:
Comment
.joins(:user)
.where.not(users: { deleted_at: nil })
.update_all("comments.deleted_at = users.deleted_at")
This won't work on Postgres since its missing a FROM clause for users.
A less performant but polyglot option is:
Comment
.joins(:user)
.where.not(users: { deleted_at: nil })
.update_all("deleted_at = ( SELECT users.deleted_at FROM users WHERE comments.id = users.id )")
This is still probably an order of magnitude better than iterating through the records in Ruby since you eliminate the traffic delay between your app server and the db.
From your comments, I think this is what you want:
Comment.where.not(user_id: nil).each { |comment| comment.update_attributes(deleted_at: comment.user.deleted_at)
Or slightly more readable:
Comment.all.each do |comment|
next unless comment.user.present?
comment.update_attributes(deleted_at: comment.user.deleted_at)
end
The code below should execute number of queries corresponding to deleted_users and without loading User and any associated Comments in memory
deleted_users_data_arr = User.only_deleted.pluck(:id, :deleted_at)
deleted_users_data_arr.each do |arr|
deleted_user_id = arr[0]
user_deleted_at = arr[1]
Comment.where(commenter_id: deleted_user_id).update_all(deleted_at: user_deleted_at)
end

How to lazy-load or eager-load the sum of associated table?

We build some object in our controller:
#sites = Site.find(:all, :conditions => conditions, :order => 'group_id ASC')
And then in the view (currently), we are doing something like:
#sites.each do |site|
%p Site Name
= site.name
- actual = site.estimates.where('date<?', Date.today).sum(:actual_cost)
%p
= actual
end
Basically like that.
And of course this fires off a query for the Sites and then a query for N sites returned. I know about eager-loading associations with #sites = Site.find(:all, :include => :estimates) but in this case it didn't matter because we're doing the SUM on the query which is special it seems.
How would I eager load the SUMs in such that I don't get all the crazy queries? Currently it's over 600...
provide your conditions & ordering in this query only, which will push the result into a Hash.
sites = Site.includes(:estimates).where('estimates.date < ? ', Date.today)
.order('sites.id ASC')
actual = 0
sites.map { |site|
site.estimates.map { |estimate| actual = actual + estimate.actual_cost }
}
From your explanation, I am assuming actual_cost is a column in estimate table.

Rails custom query based on params

I have zero or many filter params being sent from a json request.
the params may contain:
params[:category_ids"]
params[:npo_ids"]
etc.
I am trying to retreive all Projects from my database with the selected ids. Here is what I have currently:
def index
if params[:category_ids].present? || params[:npo_ids].present?
#conditions = []
#ids = []
if params["category_ids"].present?
#conditions << '"category_id => ?"'
#ids << params["category_ids"].collect{|x| x.to_i}
end
if params["npo_ids"].present?
#conditions << '"npo_id => ?"'
#ids << params["npo_ids"].collect{|x| x.to_i}
end
#conditions = #ids.unshift(#conditions.join(" AND "))
#projects = Project.find(:all, :conditions => #conditions)
else ...
This really isn't working, but hopefully it gives you an idea of what I'm trying to do.
How do I filter down my activerecord query based on params that I'm unsure will be there.
Maybe I can do multiple queries and then join them... Or maybe I should put a filter_by_params method in the Model...?
What do you think is a good way to do this?
In rails 3 and above you build queries using ActiveRelation objects, no sql is executed until you try to access the results, i.e.
query = Project.where(is_active: true)
# no sql has been executed
query.each { |project| puts project.id }
# sql executed when the first item is accessed
The syntax you are using looks like rails 2 style; hopefully you are using 3 or above and if so you should be able to do something like
query = Project.order(:name)
query = query.where("category_id IN (?)", params[:category_ids]) if params[:category_ids].present?
query = query.where("npo_ids IN (?)", params[:npo_ids]) if params[:npo_ids].present?
#projects = query
I solved this. here's my code
def index
if params[:category_ids].present? || params[:npo_ids].present?
#conditions = {}
if params["category_ids"].present?
#conditions["categories"] = {:id => params["category_ids"].collect{|x| x.to_i}}
end
if params["npo_ids"].present?
#conditions["npo_id"] = params["npo_ids"].collect{|x| x.to_i}
end
#projects = Project.joins(:categories).where(#conditions)
else
basically it stored the .where conditions in #conditions, which looks something like this when there's both categories and npos:
{:categories => {:id => [1,2,3]}, :npo_id => [1,2,3]}
Then inserting this into
Project.joins(:categories).where(#conditions)
seems to work.
If you're filtering on a has_many relationship, you have to join. Then after joining, make sure to call the specific table you're referring to by doing something like this:
:categories => {:id => [1,2,3]}

active record query conflict between :joins and :includes

Can anyone suggest an easy fix for the query below?
Workout.all(:joins => [:person, :schedule],
:conditions => ["schedule.id = ? AND people.gym_id = ?", schedule.id, gym.id ],
:order => "time DESC",
:include => [:person]),
Error message says Mysql::Error: Not unique table/alias: 'people': and then a very long sql query
When I remove :person from the :include options, the code works fine. However, then I get the "n+1 queries" problem: when I display the workouts, it generates a separate query for each person.
I am assuming that the 'people' tables from the join and include options conflict in the generated SQL. Is there a way to alias tables with active record using the find options? Or do I need to use a sql fragment for the :joins option so that I can alias the tables with AS?
I have not upgraded this app to 3.0 yet, so I can't use the "Class.joins().where()" syntax.
You can remove :person from the joins and keep it in the include, you can add fields from the people table in the :select option e.g
Workout.all(:joins => [:schedule],
:conditions => ["schedule.id = ? AND people.gym_id = ?", schedule.id, gym.id ],
:order => "time DESC",
:select => "workouts.*,people.*"
:include => [:person])

Correct find syntax for :first, :order in Rails 3?

I am currently using the following to retrieve a town name:
place = Town.find(:first, :conditions => ["name = ?", params[:place_post]])
place ||= Town.find(:first, :order => "damlev(`towns`.`name`, '" + params[:place_post] + "')")
What would be the best way to do this, named scopes etc I just feel this is well rubbish and bad practice.
Thanks!
If params[:place_post] is the name of the town:
place = Town.find(:first, :conditions => ["name = ?", params[:place_post]], :order => "name [ASC|DESC]")
Use either ASC or DESC.
Updated with ARel syntax:
place = Town.where(:name => params[:place_post]).order("name [ASC|DESC]").first()
or:
place = Town.order("name [ASC|DESC]").find_by_name(params[:place_post])
Don't know exactly what's the idea with the demlev function, but this may work:
place = Town.order("demlev(name, #{quoted_place_post]}) [ASC|DESC]").find_by_name(params[:place_post])
quoted_place_post can be Town.quote_value(params[:place_post])