I really don't think the title of this explains well of what I'm trying to do but I'm not even sure how to ask.
So I have ticket has_many tasks and task belongs_to account. I've this as a scope to return the ticket listing where an tickets task belongs to an account:
scope :for_tasks_account, lambda { |account| joins(:tasks => :account ).where("accounts.id = ?", account.id) }
but it's returning multiple of the same ticket because a ticket has multiple tasks that the account belongs to.
How can I get it to only return each ticket once rather for each task in that ticket that an account belongs to?
Thanks!
Update
I'd actually like to combine to scopes to list all that apply to the two lambdas:
scope :for_account, lambda { |account| joins(:group => :accounts ).where("accounts.id = ?", account.id) } || lambda { |account| joins(:tasks => :account ).where("accounts.id = ?", account.id) }
Is this possible? As well as the first issue.
Update 2
I've figured out how to get both of the queries to be combined but I'm still getting multiple of the same ticket in the returned query.
scope :for_group_with_account, lambda { |account| joins(:group => :accounts ).where("accounts.id = ?", account.id) }
scope :for_task_with_account, lambda { |account| joins(:tasks => :account ).where("accounts.id = ?", account.id) }
scope :for_account, lambda { |account| for_group_with_account(account) & for_task_with_account(account).select('DISTINCT id') }
I'm using DISTICNT but I still get
SQLite3::SQLException: ambiguous column name: id: SELECT DISTINCT id FROM "tickets" INNER JOIN "groups" ON "groups"."id" = "tickets"."group_id" INNER JOIN "assignments" ON "groups"."id" = "assignments"."group_id" INNER JOIN "accounts" ON "accounts"."id" = "assignments"."account_id" INNER JOIN "tasks" ON "tasks"."ticket_id" = "tickets"."id" INNER JOIN "accounts" "accounts_tasks" ON "accounts_tasks"."id" = "tasks"."account_id" WHERE ("tickets"."archived" IS NULL) AND (accounts.id = 20) LIMIT 20 OFFSET 0
Thanks again!
I think you should be able to use "distinct" in this scenario.
scope :for_tasks_account, lambda { |account| joins(:tasks => :account ).where("accounts.id = ?", account.id).select('distinct accounts.id') }
Related
I have a model to keep 2 users "user" and "worker".
create_table "action_logs", :force => true do |t|
t.integer "user_id"
t.integer "worker_id"
t.string "text_log"
end
class ActionLog < ActiveRecord::Base
belongs_to :user
belongs_to :worker, :class_name => 'User'
end
Now, I want to write a scope using "user" and "worker" on this model.
scope :not_inhouse, -> {
includes(:user).where( "users.inhouse = ?", false).
includes(:worker).where( "workers.inhouse = ?", false)
}
But it doesn't work.
ActiveRecord::StatementInvalid:
SQLite3::SQLException: no such column: workers.inhouse:
SELECT COUNT(DISTINCT "action_logs"."id") FROM "action_logs" LEFT OUTER JOIN "users" ON "users"."id" = "action_logs"."user_id" LEFT OUTER JOIN "users" "workers_action_logs" ON "workers_action_logs"."id" = "action_logs"."worker_id" WHERE (users.inhouse = 'f') AND (workers.inhouse = 'f')
It seems ActiveRecord doesn't handle class_name as I expected.
Is there any way to write a scope using class_name?
Thanks for comments. Now I understood 2 points.
Diffrence between includes and joins.
ActiveRecords name joined table name, independently of class_name.
So I can write the scope like this:
scope :not_inhouse, -> {
joins(:user).where( "users.inhouse = ?", false).
joins(:worker).where( "workers_action_logs.inhouse = ?", false)
}
or
scope :not_inhouse, -> {
joins(:user).where( "users.inhouse = ?", false).
joins('INNER JOIN "users" "workers" ON workers.id = action_logs.worker_id').
where( "workers.inhouse = ?", false)
}
I'm trying to search a column from another table using three tables in relationship.
In my view policy_vehicles I have a text_field_tag and want to search by "raz_soc".
My tables are:
Policy_vehicles
|id| |policy_id|
integer integer
100 1
200 2
Policies
|id| |client_id|
integer integer
1 1
2 2
Clients
|id| |raz_soc|
integer varchar(255)
1 MARATEX SAC
2 ATT
This is my controller:
class PolicyManagement::PolicyController < ApplicationController
def generate_print_per_vehicle
params[:search_policy_id] = Policy.find(:all,:joins => :client ,:conditions => ['raz_soc LIKE ?',"%#{params[:search_raz_soc]}%" ])
#policies= PolicyVehicle.find(:all,:joins => :policy, :conditions => ['policy_id = ?',params[:search_policy_id] ])
end
end
This is my model:
class Policy < ActiveRecord::Base
belongs_to :client
has_many :policy_vehicles
end
class PolicyVehicle < ActiveRecord::Base
belongs_to :policy
end
class Client < ActiveRecord::Base
has_many :policies
end
This is my view where I'm trying to find by a column from another table:
<% form_tag :controller=>"policy_management/policy",:action=>"generate_print_per_vehicle" do %>
Society:
<%= text_field_tag "search_raz_soc",params[:search_raz_soc] %>
<%= submit_tag "Buscar", :name => nil %>
<% end %>
My logs are:
Mysql::Error: Operand should contain 1 column(s): SELECT `policy_vehicles`.* FROM `policy_vehicles` INNER JOIN `policies` ON `policies`.id = `policy_vehicles`.policy_id WHERE (policy_id = 166,1540,2822,4074)
It should be a search like this:
Policy Load (3.1ms) SELECT `policies`.* FROM `policies` INNER JOIN `clients` ON `clients`.id = `policies`.client_id WHERE (raz_soc = 'MARATEX SAC')
PolicyVehicle Load (0.3ms) SELECT `policy_vehicles`.* FROM `policy_vehicles` INNER JOIN `policies` ON `policies`.id = `policy_vehicles`.policy_id WHERE (policy_id = 1)
I tried this but is not working:
#controller
#policy = Policy.find(:all,:joins => :client ,:conditions => ['raz_soc LIKE ?',params[:search_raz_soc] ])
#policies= PolicyVehicle.find(:all,:joins => :policy, :conditions => ['policy_id = ?',#policy ])
#logs
Policy Load (3.1ms) SELECT `policies`.* FROM `policies` INNER JOIN `clients` ON `clients`.id = `policies`.client_id WHERE (raz_soc = 'MARATEX SAC')
PolicyVehicle Load (0.3ms) SELECT `policy_vehicles`.* FROM `policy_vehicles` INNER JOIN `policies` ON `policies`.id = `policy_vehicles`.policy_id WHERE (policy_id = 70353610714140)
I'm trying to do something like this
select * from policy_vehicles where policy_id
IN ( SELECT id FROM policies WHERE
client_id IN (SELECT id FROM clients raz_soc = ?) )
Can someone can help me please?
Try this (I have explained what the queries return so make sure that this is what you want from them)
#policies = Policy.find(:all,:joins => :client ,
:conditions => ['raz_soc LIKE ?',"%#{params[:search_raz_soc]}%"] )
# returns an array of policy records based on the raz_soc condition on client
#policy_vehicles = PolicyVehicle.find(:all,:joins => :policy,
:conditions => ['policy_id IN (?)',#policies] )
# returns an array of policy_vehicles that are associated with any record in the #policies array
Other option is to do it in a single query as:
#policy_vehicles = PolicyVehicle.joins(:policy => :client).
where('clients.raz_soc LIKE ?',"%#{params[:search_raz_soc]}%")
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, ....
I have a controller with two different actions, but both need this same code, which is a little long, how can I allow them access to this same behavior but keep it DRY?
#list = Contact.find :all,
:select => "companies.name AS co_name,
companies.id AS comp_id,
COUNT(contact_emails.id) AS email_count,
COUNT(contact_calls.id) AS call_count,
COUNT(contact_letters.id) AS letter_count,
COUNT(contact_postalcards.id) AS postalcard_count",
:conditions => ['contact_emails.date_sent < ? and contact_emails.date_sent > ?',
report_end_date, report_start_date],
:joins => [
"LEFT JOIN companies ON companies.id = contacts.company_id",
"LEFT JOIN contact_emails ON contact_emails.contact_id = contacts.id",
"LEFT JOIN contact_letters ON contact_letters.contact_id = contacts.id",
"LEFT JOIN contact_postalcards ON contact_postalcards.contact_id = contacts.id",
"LEFT JOIN contact_calls ON contact_calls.contact_id = contacts.id"
],
#:group => "companies.id"
:group => "companies.name"
puts #list[0].attributes.inspect
You should move this code to model:
# Contatct model
def self.get_list(report_start_date, report_end_date)
self.find :all,
:select => "companies.name AS co_name,
companies.id AS comp_id,
COUNT(contact_emails.id) AS email_count,
COUNT(contact_calls.id) AS call_count,
COUNT(contact_letters.id) AS letter_count,
COUNT(contact_postalcards.id) AS postalcard_count",
:conditions => ['contact_emails.date_sent < ? and contact_emails.date_sent > ?',
report_end_date, report_start_date],
:joins => [
"LEFT JOIN companies ON companies.id = contacts.company_id",
"LEFT JOIN contact_emails ON contact_emails.contact_id = contacts.id",
"LEFT JOIN contact_letters ON contact_letters.contact_id = contacts.id",
"LEFT JOIN contact_postalcards ON contact_postalcards.contact_id = contacts.id",
"LEFT JOIN contact_calls ON contact_calls.contact_id = contacts.id"
],
#:group => "companies.id"
:group => "companies.name"
end
Then you can use it in controllers:
#list = Contact.get_list(report_start_date, report_end_date)
Probably you can also split it to smaller parts and use scopes and defined associations instead of writing all of it on your own.
I would add a function for generating the count and join sql:
class Contact < ActiveRecord::Base
def self.get_list(report_start_date, report_end_date)
all(:select => "companies.name AS co_name,
companies.id AS comp_id,
#{table_count_col(
:contact_emails,
:contact_calls,
:contact_letters,
:contact_postalcards
)}",
:conditions => ['contact_emails.date_sent < ? AND
contact_emails.date_sent > ?',
report_end_date, report_start_date],
:joins => join_table(
:companies,
:contact_emails,
:contact_letters,
:contact_postalcards,
:contact_calls
),
)
end
end
Where table_count_col and table_join are static methods inside Contact class:
def self.table_count_col(*args)
args.collect do |table|
count_col = "#{table.to_s.gsub(/^contact_/, '').singularize}_count"
"COUNT(#{table}.id) AS #{count_col}"
end.join(",")
end
def self.table_join(*args)
args.collect do |table|
"LEFT JOIN #{table} ON #{table}.id = contacts.company_id"
end.join(",")
end
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.)