how to get name from a table corresponding to the id taken from different table in zend framework - sql

i written a query in php zend framework where i joined three table employee, products, bid.
in product table i have a column buyer, where am saving buyer employee id, and am displaying details of sellername(taken from employee nname, column name ename, ) and also need to display buyer name by taking buyer id from the product table and matching ename from employee table.
my sql query is given below.
except the above feature its working fine.
$oSelect = $this->select()
->setIntegrityCheck(false)
->from(array("p" => "products","b" => "bid"), ('*'))
->joinLeft(array("b" => "bid"), "b.product_id=p.product_id", array('bid_id','bid_amount'))
->joinInner(array("e" => "employees"), "e.employee_id=p.employee_id",array('ename'))
->where("p.verified = ?", "Yes")
->where("p.sold_out = ?", "Yes")
->group('p.product_id')
->having("p.sale_end_date >= ?", date("Y-m-d"));
so anyone help me to add the above feature to this query.

Select query will be like this for displaying buyername
$oSelect = $this->select()
->setIntegrityCheck(false)
->from(array("p" => "products","b" => "bid"), ('*'))
->joinLeft(array("b" => "bid"), "b.product_id=p.product_id", array('bid_id','bid_amount'))
->joinLeft(array("e" => "employees"), "e.employee_id=p.employee_id",array('ename'))
->joinLeft(array("e1" => "employees"), "e1.employee_id=p.buyer_id",array('buyer_name' => 'ename'))
->where("p.verified = ?", "Yes")
->where("p.sold_out = ?", "Yes")
->group('p.product_id')
->having("p.sale_end_date >= ?", date("Y-m-d"));

Related

Rails SQL in controller

I'm using Xeditable and RABL in a Rails app.
I have a workorder that belongs to a workgroup.
I want to assign the workorder to an employee in that workgroup.
I'm using this as the source in the Xeditable:
data-source="/employees.json?workgroup=<%= workorder.workgroup.id%>"
And this is the code I'm trying in the employee controller:
def index
#employees = Employee.order(:first_name)
#employees = Employee.joins(:empgroups).where(:workgroup_id => params[:workgroup]) if params[:workgroup].present?
end
This is the SQL that gets generated:
SELECT "employees".* FROM "employees" INNER JOIN "empgroups" ON "empgroups"."employee_id" = "employees"."id" WHERE "employees"."workgroup_id" = 2
The issue is the WHERE should be `WHERE "empgroups"."workgroup_id" = 2
How do I change this line of code?
#employees = Employee.joins(:empgroups).where(:workgroup_id => params[:workgroup]) if params[:workgroup].present?
Thanks for the help!
You can use nested hash syntax:
#employees = Employee.joins(:empgroups).where(
empgroups: { workgroup_id: params[:workgroup] }
) if params[:workgroup].present?
This should work:
Employee.joins(:empgroups).where(:empgroups => {:workgroup_id => params[:workgroup]})
The :workgroup_id is actually an attribute/column on a joined table, and not on the base table that you are querying from, so you need to specify where that column is located during the where clause.
Remember that in a pinch you can also type in SQL for the where clause, but will also have to remember to properly reference the right table.
Employee.joins(:empgroups).where("empgroups.workgroup_id = ?", params[:workgroup])

Is it the right way to do union query after joins in rails 3.2?

There are 3 models log (which belongs to customer), customer and project in rails 3.2 app. Both customer and project have sales_id field. Here is the query we want to do:
return the following logs for customers 1) logs for customers whose sales_id is equal to session[:user_id] and 2) logs for customers whose projects' sales_id is equal to session[:user_id]
The rails query for 1) could be:
Log.joins(:customer).where(:customers => {:sales_id => session[:user_id]})
Rails query for 2) could be:
Log.joins(:customer => :projects).where(:projects => {:sales_id => session[:user_id})
To combine the queries above, is it the right way to do the following?
Log.joins([:customer, {:customer => :projects}]).where('customers.sales_id = id OR projects.sales_id = id', id: session[:user_id])
Chapter 11.2.4 in http://guides.rubyonrails.org/v3.2.13/active_record_querying.html talks about an interesting query case. We haven't tested the query above yet. We would like to know if the union query above is indeed correct.
Rails doesn't support union natively. In your case, I think it doesn't need union, just use left outer join.
Log.joins('left outer JOIN `customers` ON `customers`.`id` = `logs`.`customer_id`
left outer JOIN `projects` ON `projects`.`customer_id` = `customers`.`id`').where('customers.sales_id = :id OR projects.sales_id = :id', id: session[:user_id]).distinct

Are those two queries the same in rails 3.2.12?

We need to retrieve logs for customer comm record in our rails app. The condition in plain english is logs for customer_comm_record based on:
#1. the sales_id in customer comm record's customers is equal to current user id
#2. the sales_id in customer comm record's customer's project is equal to the current user id.
The SQL code for #1 could be (SQL#1):
Log.joins(:customer_comm_record =>:customer).where('customers.sales_id = ?', session[:user_id])
SQL code for #2 could be (SQL#2):
Log.joins(:customer_comm_record =>{ :customer => :projects}).where('projects.sales_id = ?', session[:user_id])
Here is what we come up (SQL#3) in one line:
Log.joins(:customer_comm_record =>{ :customer => :projects}).where('customers.sales_id = ? OR projects.sales_id = ?', session[:user_id], session[:user_id])
The SQL code returned by above in rails console is (replacing session[:user_id] with 1) :
SELECT "logs".* FROM "logs" INNER JOIN "customer_comm_records" ON "customer_comm_records"."id" = "logs"."customer_comm_record_id" INNER JOIN "customers" ON "customers"."id" = "customer_comm_records"."customer_id" INNER JOIN "projects" ON "projects"."customer_id" = "customers"."id" WHERE (customers.sales_id = 1 OR projects.sales_id = 1)
The question is weather SQL#3 == SQL#1 UNION SQL#2. Can someone answer the question? If it is not, what's the right one? Thanks.
The only differences are:
The first method will return duplicate entries where the sales_id for the customer and project both match the users id.
The second method might not return records where a customer with a matching sales_id does not have a project (no projects.customer_id for that customer).
The last difference only matters if a customer does not necessarily have any projects. Otherwise, the second method is better because it avoids dealing with duplicates.
If a customer does not always have a project, a simple workaround would be to use includes instead of joins:
Log.includes(:customer_comm_record =>{ :customer => :projects}).
where('customers.sales_id = :id OR projects.sales_id = :id', id: session[:user_id])
This will force a LEFT JOIN and will return customers regardless of whether they are associated with a project.

Complex rails find ordering

I am trying to do a find which orders results by their house name and then by the customer's last name.
Customer.find(:all,
:conditions =>['customers.id IN (?)', intersection],
:joins => 'JOIN histories ON histories.customer_id = customers.id
JOIN houses ON histories.house_id = houses.id',
:order => "houses.name ASC, customers.last_name ASC",
:select => "customers.*, histories.job_title, houses.name"
)
My problem is this will return every history related to each customer.
if I add AND histories.finish_date IS NULL
This will prevent every history for the selected customer being returned but it will also stop customers in the intersection who have no history or a finish_date set from being returned.
Basically I need every customer in the intersection returned once with there current house name(if they have one) and then ordered by their house name and then their last name.
So is there a way of doing this?
Here is an example
customer
id last_name
1 franks
2 doors
3 greens
histories
id finish_date house_id customer_id
1 NULL 1 1
2 NULL 2 2
3 11/03/10 2 1
4 22/04/09 1 2
NULL = current house
houses
id name
1 a
2 b
Results
intersection = 1,2,3
last_name house
franks a
doors b
greens NULL
Thanks
I think you need to use outer joins.
For example, this should work:
Customer.find(:all,
:conditions =>['customers.id IN (?) and histories.finish_date is null', intersection],
:joins => 'LEFT OUTER JOIN histories ON histories.customer_id = customers.id
LEFT OUTER JOIN houses ON histories.house_id = houses.id',
:order => "houses.name ASC, customers.last_name ASC",
:select => "customers.*, histories.job_title, houses.name"
)
If you've got an association between Customer and History and between History and House you should be able to do :include => [:histories => :house] instead of the :joins option.
The only other thing is that the customers with no house will appear first in the list due to NULL being earlier in the order than a non-NULL value. You might want to try an order option like this :
:order => 'isnull(houses.name), houses.name, customers.last_name'
to achieve what you specified.
IMO it's simpler to do the sorting logic in Rails instead of the database:
customers = Customer.find(:all, :conditions => { :id => intersection }, :include => [ { :histories => :houses } ])
customers.sort_by { |c| c.last_name }
customers.sort_by do |c|
current_house = c.histories.find_by_finish_date(nil) # Returns nil if no matching record found
if current_house
current_house.name
else
''
end
end
Explanations
:conditions can take an hash { :column_name => array } which translates into your IN where-condition
:include pre-loads (eager loading) the tables if the corresponding associations exist. To put it another way: :joins creates INNER JOINs, while :include creates LEFT JOINs. Here we will left join histories and again left join houses. You could omit this :include tag, in which case rails does a new query each time you access a histories or houses property.
sort_by allows to define a custom sort criteria.
find_by_finish_date is one of rails' magic methods; it is equivalent to h.find(:conditions => {:finish_date => nil })
How to output: Just output all of them in your view. If he does not have histories, customer.histories is an empty array.

Complex Join Queries in Rails

I have 3 tables - venues, users, and updates (which have a integer for rating) - and I want to write a query that will return a list of all my venues as well as their average ratings using only the most recent update for each person, venue pair. For example, if user 1 rates venue A once at 9 am with a 4, and then rates it again at 5 pm with a 3, I only want to use the rating of 3, since it's more recent. There are also some optional conditions, such as how recent the updates must be, and if there is an array of user ids the users must be within.
Does anybody have a suggestion on what the best way to write something like this is so that it is clean and efficient? I have written the following named_scope which should do the trick, but it is pretty ugly:
named_scope :with_avg_ratings, lambda { |*params|
hash = params.first || {}
has_params = hash[:user_ids] || hash[:time_ago]
dir = hash[:dir] || 'DESC'
{
:joins => %Q{
LEFT JOIN (select user_id, venue_id, max(updated_at) as last_updated_at from updates
WHERE type = 'Review' GROUP BY user_id, venue_id) lu ON lu.venue_id = venues.id
LEFT JOIN updates ON lu.last_updated_at = updates.updated_at
AND updates.venue_id = venues.id AND updates.user_id = lu.user_id
},
:select => "venues.*, ifnull(avg(rating),0) as avg_rating",
:group => "venues.id",
:order => "avg_rating #{dir}",
:conditions => Condition.block { |c|
c.or { |a|
a.and "updates.user_id", hash[:user_ids] if hash[:user_ids]
a.and "updates.updated_at", '>', hash[:time_ago] if hash[:time_ago]
} if has_params
c.or "updates.id", 'is', nil if has_params
}
}
}
I include the last "updates.id is null" condition because I still want the venues returned even if they don't have any updates associated with them.
Thanks,
Eric
Yikes, that looks like a job for find_by_sql to me. When you're doing something that complex, I find it's best to take the job away from ActiveRecord and DIY.