IN clause in :conditions in rails - ruby-on-rails

I am working in rails 2, I want to execute Query
PunchingInformation.all(
:select => "users.id, login, firstname, lastname,
sec_to_time(avg(time_to_sec(punching_informations.punch_in_time))) as 'avg_pit',
sec_to_time(avg(time_to_sec(punching_informations.punch_out_time))) as 'avg_pot'",
:joins => :user,
:group => "users.id",
:conditions => {
"punching_informations.date between '#{start_date}' and '#{end_date}'",
["punching_informations.user_id IN (?)", employees.map { |v| v.to_i } ]
}
)
But it always return error like
Mysql::Error: Unknown column 'punching_informations.date between '2012-09-01' and '2012-09-25'' in 'where clause': SELECT users.id,login, firstname,lastname, sec_to_time(avg(time_to_sec(punching_informations.punch_in_time))) as 'avg_pit',
sec_to_time(avg(time_to_sec(punching_informations.punch_out_time))) as 'avg_pot' FROM punching_informations INNER JOIN users ON users.id = punching_informations.user_id AND (users.type = 'User' OR users.type = 'AnonymousUser' ) WHERE (punching_informations.date between '2012-09-01' and '2012-09-25' IN ('punching_informations.user_id IN (?)','--- \n- 28\n- 90\n')) GROUP BY users.id
Need your help.

It is a bit unclear what you meant (you have array, but taken in curly braces {} like a hash), but it seems ruby treats first string ("punching_informations.date between '#{start_date}' and '#{end_date}'") as a column, and second array, as array of expected values, thus making the invalid IN condition.
Perhaps it would work if rewritten as
:conditions => {
[ "(punching_informations.date between '#{start_date}' AND '#{end_date}') AND punching_informations.user_id IN (?)", employees.map { |v| v.to_i } ]
}
or even better
:conditions => {
[ "(punching_informations.date between ? AND ?) AND punching_informations.user_id IN (?)", start_date, end_date, employees.map { |v| v.to_i } ]
}

add punching_informations.date and punching_informations.user_id in select
:select => "punching_informations.date, punching_informations.user_id, users.id, ....

Related

Union of 2 named_scopes

I have the following 2 named_scopes:
named_scope :scope1, lambda { |pi_id|
{ :select => "DISTINCT REF_PRO.*",
:joins => "INNER JOIN LNK_PRO ON LNK_PRO.PR_PRO_FK = REF_PRO.RPR_ID
INNER JOIN EMI_SUBMISSION on EMI_SUBMISSION.SUB_ID = LNK_PRO.PR_SUBMISSION_FK
INNER JOIN EMI_PERSON on EMI_PERSON.PER_ID = EMI_SUBMISSION.SUB_PI_FK ",
:conditions=>["EMI_PERSON.PER_ID = ? ", pi_id],
:group => "REF_PRO.RPR_ID"
}
}
named_scope :scope2, lambda { |pi_id|
{ :select => "REF_PRO.*",
:joins => "INNER JOIN REF_USER ON REF_USER.USR_ID = REF_PRO.RPR_CREATED_BY
INNER JOIN LNK_USER_PI on LNK_USER_PI.USP_USER_FK = REF_USER.USR_ID ",
:conditions=>["LNK_USER_PI.USP_PI_ID = ? ", pi_id]
}
}
I need to join their results.
Is there a way of doing a union of the 2 resultsets? If not, how can I modify one named_scope so that it returns me the combined results of the above named scopes?
Thanks a lot for your help
You can do this:
Model.scope1+Model.scope2

ActiveRecord OR clause in scoped query

statuses = %w(sick healthy hungry)
query = User.scoped(:joins => 'left outer join pets on pets.user_id = users.id', :conditions => { 'pets.id' => nil, 'users.job_status' => stati })
Given the code above, is it possible to add an OR clause to the :conditions to say something like
where (pets.id is NULL AND users.status IN ('sick', 'healthy', 'hungry')) OR (users.gender = 'male')
You can do the following using the MetaWhere gem
statuses = %w(sick healthy hungry)
query = User.include(:pets).where(
('pets.id' => nil, 'users.job_status' => statuses) |
('users.gender' => 'male')
)
The | symbol is used for OR condition.
PS : The include directive uses LEFT OUTER JOIN so there is no need to hand code the JOIN.
You could use an SQL condition instead of a Hash condition:
query = User.scoped(
:joins => 'left outer join pets on pets.user_id = users.id',
:conditions => [
'(pets.id is null AND users.status IN (?)) OR (users.gender = ?)',
statuses, 'male'
]
)
Or:
query = User.scoped(
:joins => 'left outer join pets on pets.user_id = users.id',
:conditions => [
'(pets.id is null AND users.status IN (:statuses)) OR (users.gender = :gender)',
{ :status => statuses, :gender => 'male' }
]
)
The downside is that you have to avoid trying pets.is = NULL by hand.

Ruby: group an array of ActiveRecord objects in a hash

I'd like to group an array of ActiveRecord objects into a hash with an interface that's simple to query after an SQL statement that looks like the following:
SELECT name,value from foo where name IN ('bar', 'other_bar') LIMIT 2;
After that query, I want a hash where I can go:
foo[:bar] # output: value1
foo[:other_bar] # output: value2
What's the best way to collect the objects with ActiveRecord and group them so I can use the interface above?
In Rails 2
foos = Foo.all :select => "name, value",
:conditions => ["name in (?)", %w(bar other_bar)],
:limit => 2
In Rails 3
foos = Foo.where("name in (?)", %w(bar other_bar)).select("name, value").limit(2)
Then
foo = Hash[foos.map { |f| [f.name.to_sym, f.value] }]
or
foo = foos.inject({}) { |h, f| h[f.name.to_sym] = f.value; h }
or in Ruby 1.9
foo = foos.each_with_object({}) { |f, hash| hash[f.name.to_sym] = f.value }
If I understood you correctly:
foo = Hash[Foo.find(:all, :limit => 2, :select => "name, value", :conditions => ["name in ('bar', 'other_bar')"]).map { |s| [s.name.intern, s.value] }]
Hash[result.map { |r| [r[:name].to_sym, r[:value]] } ]
models.inject({}) {|h,m| h[ m.name.to_sym ] = m.value; h }

named scope vs. find_by_sql (specific example)

Just out of curiosity, does anyone know a better way of building the following collection using named scopes (as opposed to find_by_sql)?
#available = Workflow.find_by_sql(["
SELECT workflows.id FROM workflows
WHERE workflows.project_id = ? AND workflows.status < 5 AND
( workflows.created_by = ? OR workflows.id IN
(
SELECT workflow_id FROM workflow_histories
INNER JOIN workflow_recipients on workflow_histories.id = workflow_recipients.workflow_history_id
WHERE workflow_recipients.recipient_id = ? AND workflow_recipients.recipient_type = ?
)
)", project.id, #current_user.id, #current_user.id , 'USER'])
I haven't tested this, but I think it would work:
named_scope :available, lambda { |user_id, project_id|
{ :select => :id,
:conditions => [ "project_id = :project_id AND status < 5 AND
(created_by = :user_id OR id IN (
SELECT workflow_id FROM workflow_histories
INNER JOIN workflow_recipients ON workflow_histories.id = workflow_recipients.workflow_history_id
WHERE workflow_recipients.recipient_id = :user_id AND workflow_recipients.recipient_type = :recipient_type
)",
{ :user_id => user_id,
:project_id => project_id,
:recipient_type => "USER"
}
]
}
}
(A previous version of my answer breaks the sub-select out into its own query, which I think is unnecessary.)

Rails, working with named_scope

I need to select some dynamic price ranges submitted from a search form. How should I approach this with scopes? I am looking for something like this
Painting.price_range(['1..500', '2000..5000'])
SELECT * FROM paintings WHERE price BETWEEN 1..500 **OR** BETWEEN 2000..5000 etc.
Best regards.
Asbjørn Morell.
named_scope :price_range, :conditions => ["(price BETWEEN 1 AND 500) OR (price BETWEEN 2000 AND 5000)"]
OR
named_scope :price_range, :conditions => ["(price ?) OR (price ?)", (1..500).to_s(:db), (2000..5000).to_s(:db)]
Dynamic
named_scope :price_between, lambda { |from, to| { :conditions => ['price > ? AND price <= ?', from, to] } }
named_scope :price_between, lambda { |from, to| { :conditions => ['price BETWEEN ? AND ?', from, to] } }
->
MyModel.price_between(1,100)
You'll need to use a lambda on the named_scope. The following should work:
named_scope :price_range, lambda { |ranges|
{
:conditions => ["(" +
ranges.collect {"price between ? and ?"}.join(" or ") +
")"] +
ranges.collect {|r| [r.min, r.max]}.flatten
}
}
The first ranges.collect creates as many "between ? and ?" checks as you have ranges and then the second ranges.collect flattens out the ranges and adds them as values to be sanitized into the conditions. I've stuck brackets round the ors just to be on the safe side.

Resources