How to make SQL query from many tables on Rails? - mysql

Thank you for read my quetsion.
I want to know which is the best or correct way to express queries in Rails which make many joins and select attributes from multiple tables:
SELECT
ffca.name,
ffc.name,
ft.title,
concat(s.first_name, ' ', s.last_name),
pf.name,
ft.amount,
date_format(ft.created_at,'%d/%m/%y')
FROM
finance_transactions ft
INNER JOIN students s ON ft.student_id = s.id
INNER JOIN payment_forms pf ON ft.payment_form_id = pf.id
INNER JOIN finance_fees ff ON ft.finance_fees_id = ff.id
INNER JOIN finance_fee_collections ffc ON ff.fee_collection_id = ffc.id
INNER JOIN finance_fee_categories ffca ON ffc.fee_category_id = ffca.id;
If you need extra info, please feel free to ask me.
Thank you very much.

e.g
your model is finance_transactions
finace_transaction = FinanceTransaction.find_by_sql(
"SELECT
ffca.name as name,
ffc.name as name1,
ft.title as title,
concat(s.first_name, ' ', s.last_name) as full_name,
pf.name as pf_name,
ft.amount as count,
date_format(ft.created_at,'%d/%m/%y') as time
FROM
finance_transactions ft
INNER JOIN students s ON ft.student_id = s.id
INNER JOIN payment_forms pf ON ft.payment_form_id = pf.id
INNER JOIN finance_fees ff ON ft.finance_fees_id = ff.id
INNER JOIN finance_fee_collections ffc ON ff.fee_collection_id = ffc.id
INNER JOIN finance_fee_categories ffca ON ffc.fee_category_id = ffca.id"
)
the table finance_transactions table must have column name, name1,title, full_name,pf_name, count, time
and then you can use:
finace_transaction.name1 gets ffc.name

You might want to provide a method in your model class as a wrapper
class FinanceTransaction < ActiveRecord::Base
[...]
def self.find_fee_transactions
self.find_by_sql(<<-SQL)
SELECT
ffca.name as name,
ffc.name as name1,
ft.title as title,
concat(s.first_name, ' ', s.last_name) as full_name,
pf.name as pf_name,
ft.amount as count,
date_format(ft.created_at,'%d/%m/%y') as time
FROM
finance_transactions ft
INNER JOIN students s ON ft.student_id = s.id
INNER JOIN payment_forms pf ON ft.payment_form_id = pf.id
INNER JOIN finance_fees ff ON ft.finance_fees_id = ff.id
INNER JOIN finance_fee_collections ffc ON ff.fee_collection_id = ffc.id
INNER JOIN finance_fee_categories ffca ON ffc.fee_category_id = ffca.id"
)
SQL
end
end

Related

How to query multiple weak tables using a join

I have a database with two strong entities, and three weak entities. The weak entities only have foreign key references to the primary key of the strong tables they related to, to create an association between the tables.
I have a query selecting from one table the way I want, but am not sure how to go about selecting from all three tables at once and displaying the data in one query.
ERD:
I have tried many different joins and can not get them to work properly. This select displays the information that I want for one table:
select f.FilmName, concat(p.FirstName, ' ', p.LastName) as Producer
from filmtable as f
inner join persontable as p
inner join produced;
I would like to be able to select and display all the information from all three weak tables, in a format similar to:
FilmName, Producer, ScreenWriter, Director.
You need:
select f.FilmName, concat(ppr.FirstName, ' ', ppr.LastName) as Producer,
concat(psw.FirstName, ' ', psw.LastName) as ScreenWriter,
concat(pdir.FirstName, ' ', pdir.LastName) as Director
from filmtable as f
left join produced as prod ON prod.FilmID = f.FilmID
left join Directed as dir ON dir.FilmID = f.FilmID
left join ScreenWote as sw ON sw.FilmID = f.FilmID
left join persontable as ppr ON prod.PersonID = ppr.personID
left join persontable as psw ON sw.PersonID = psw.personID
left join persontable as pdir ON dir.PersonID = pdir.personID;
Alternate solution is to use sub queries in SELECT statement to achieve all 3 parties i.e. Producer, Writer and Director, in one row.
SELECT f.FilmName,
(
SELECT concat(p.FirstName, ' ', p.LastName)
FROM persontable AS pt
JOIN produced AS a
ON pt.PersonID = a.PersonID
WHERE p.FilmID = f.FilmID
) AS producer,
(
SELECT concat(p.FirstName, ' ', p.LastName)
FROM persontable AS pt
JOIN screenwrote AS a
ON pt.PersonID = a.PersonID
WHERE p.FilmID = f.FilmID
) AS ScreenWriter,
(
SELECT concat(p.FirstName, ' ', p.LastName)
FROM persontable AS pt
JOIN directed AS a
ON pt.PersonID = a.PersonID
WHERE p.FilmID = f.FilmID
) AS Director
FROM filmtable AS f

mySQL: Get records union from same table

I have 2 tables a car model and car parts. Both are related with a partfitment table whose only fields are the primary keys modelID and partID.
I am trying to display the parts that fit the model car I selected.
The following works fine:
SELECT tblmodel.modelID,
tblmodel.model,
tblparts.part,
tblparts.part_number,
tblparts.description,
tblparts.list_price
FROM (tblmodel
INNER JOIN tblpartfitment
ON tblmodel.modelID = tblpartfitment.modelID)
INNER JOIN tblparts
ON tblpartfitment.partID = tblparts.partID
WHERE tblmodel.modelID = 1;
The tblparts table has the tinyint field named universal_part. I am trying to include all the parts that have universal_part = true in my original query.
In other words always return all parts that are universal and those that are for the modelID specified.
I tried using a union, but get errors.
I think you can just include the logic in the ON clause:
SELECT m.ModelID, m.Model, p.part, p.part_number, p.description, p.list_price
FROM tblmodel m INNER JOIN
tblpartfitment pf
ON m.ModelID = pf.modelID INNER JOIN
tblparts p
ON pf.partID = p.partID OR p.universal_part = 1
WHERE m.ModelID = 1;
EDIT:
You may be best off using union:
SELECT m.ModelID, m.Model, p.part, p.part_number, p.description, p.list_price
FROM tblmodel m INNER JOIN
tblpartfitment pf
ON m.ModelID = pf.modelID INNER JOIN
tblparts p
ON pf.partID = p.partID AND p.universal_part <> 1
WHERE m.ModelID = 1
UNION ALL
SELECT m.ModelID, m.Model, p.part, p.part_number, p.description, p.list_price
FROM tblmodel m CROSS JOIN
tblparts p
WHERE p.universal_part = 1;

How do I return a single row as well as multple entries using group_concat in MySQL

I hope this one is a relatively easy one to solve, but here is the problem I'm faced with:
SQL Query:
SELECT
job.job_id
,job_reference
,job_title
,jc.name as job_category_name
,ps.name as position_status_name
,group_concat(pt.name ORDER BY pt.position_type_id SEPARATOR ' / ') as position_type_name
,cty.name as city_name
,min.amount as min_salary
,max.amount as max_salary
,job_description
,skills_required
,additional_notes
FROM job
INNER JOIN job_category jc ON job.job_category_id = jc.job_category_id
INNER JOIN position_status ps ON job.position_status_id = ps.position_status_id
INNER JOIN job_position_type jpt ON job.job_id = jpt.job_id
INNER JOIN position_type pt ON jpt.position_type_id = pt.position_type_id
INNER JOIN city cty ON job.city_id = cty.city_id
INNER JOIN salary min ON job.min_salary_id = min.salary_id
INNER JOIN salary max ON job.max_salary_id = max.salary_id;
The query is only returning the entries where job_position_type table has multiple entries and not if job_position_type only has a single entry.
Cheers,
Tim
OK so I managed to sort out my problem pretty easily actually, here is my original SQL as specified above:
SELECT
job.job_id
,job_reference
,job_title
,jc.name as job_category_name
,ps.name as position_status_name
,group_concat(pt.name ORDER BY pt.position_type_id SEPARATOR ' / ') as position_type_name
,cty.name as city_name
,min.amount as min_salary
,max.amount as max_salary
,job_description
,skills_required
,additional_notes
FROM job
INNER JOIN job_category jc ON job.job_category_id = jc.job_category_id
INNER JOIN position_status ps ON job.position_status_id = ps.position_status_id
INNER JOIN job_position_type jpt ON job.job_id = jpt.job_id
INNER JOIN position_type pt ON jpt.position_type_id = pt.position_type_id
INNER JOIN city cty ON job.city_id = cty.city_id
INNER JOIN salary min ON job.min_salary_id = min.salary_id
INNER JOIN salary max ON job.max_salary_id = max.salary_id;
And this is what the query returns:
And the solution was just to add a group by clause so that it seperates the rows, here is my update SQL:
SELECT
job.job_id
,job_reference
,job_title
,jc.name as job_category_name
,ps.name as position_status_name
,group_concat(pt.name ORDER BY pt.position_type_id SEPARATOR ' / ') as position_type_name
,cty.name as city_name
,min.amount as min_salary
,max.amount as max_salary
,job_description
,skills_required
,additional_notes
FROM job
INNER JOIN job_category jc ON job.job_category_id = jc.job_category_id
INNER JOIN position_status ps ON job.position_status_id = ps.position_status_id
INNER JOIN job_position_type jpt ON job.job_id = jpt.job_id
INNER JOIN position_type pt ON jpt.position_type_id = pt.position_type_id
INNER JOIN city cty ON job.city_id = cty.city_id
INNER JOIN salary min ON job.min_salary_id = min.salary_id
INNER JOIN salary max ON job.max_salary_id = max.salary_id
GROUP BY pt.position_type_id;
And here is the expected result I wanted:
Thanks once again to #Adrien Brunelat for taking a look at my question.

NEGATION INNER JOIN with WHERE

Why the sql code below ignores
WHERE r.tt_schedules_id = '105'
in
SELECT DISTINCT r.load_date, u.url
FROM tt_results r
INNER JOIN url_lp u
ON u.lp_id <> r.lp_id
INNER JOIN tt_schedules s
ON s.tt_schedules_id = r.tt_schedules_id
WHERE r.tt_schedules_id = '105'
AND (DATE(NOW()) - DATE(r.load_date) <= 7)
?
Basically, it returns everything with any
tt_schedules_id
There are 5 tables:
url_lp, tt_results (table having foreign keys in url_lp, tt_schedules and
tt_tags), tt_schedules, tt_tags(not used) and tt_schedules_url_lp_hub
If you draw the scheme on the paper you should be clear.
I fixed it:
SELECT u.url FROM url_lp u
INNER JOIN tt_schedules_url_lp_hub hub
ON hub.lp_id = u.lp_id
INNER JOIN tt_schedules s
ON hub.tt_schedules_id = s.tt_schedules_id
WHERE s.tt_schedules_id = '105'
AND u.lp_id NOT IN
(SELECT r.lp_id FROM tt_results r WHERE r.tt_schedules_id = '105')
Cheers.

slow query with joins

Please am having difficulty in optimizing this query. What am trying to achieve is to join about 8 tables, of which only about 3 of the tables contains large data (1.5m records). This query returns expected records but is taking 1min to run which is bad.
I know it can be optimized to perform far better, pls i need assistance from you experts. I have index on the fields used for join already.
SELECT topic_id,
topic_title,
unit_name_abbrev,
sch_name_abbrev,
picture_small_url AS thumbnail,
profile_pix_upload_path,
first_name,
last_name,
topic_poster,
topic_replies,
topic_views,
topic_last_post_time AS topic_post_time,
sch_sub_forum_id
FROM (_sch_forum_topics
INNER JOIN _users
ON ( _users.userid = _sch_forum_topics.topic_poster )
INNER JOIN _profile
ON _profile.userid = _users.userid
INNER JOIN _class
ON _users.classid = _class.classid
INNER JOIN _level
ON _class.level_id = _level.id
INNER JOIN _unit
ON _class.unitid = _unit.unitid
INNER JOIN _department
ON _unit.deptid = _department.deptid
INNER JOIN _faculty
ON _department.facid = _faculty.facid
INNER JOIN _university
ON _faculty.schid = _university.schid)
WHERE _sch_forum_topics.sch_sub_forum_id = 4
ORDER BY _sch_forum_topics.topic_last_post_time DESC
LIMIT 0, 15
Try to filter before making JOIN's.
SELECT topic_id,
topic_title,
unit_name_abbrev,
sch_name_abbrev,
picture_small_url AS thumbnail,
profile_pix_upload_path,
first_name,
last_name,
topic_poster,
topic_replies,
topic_views,
topic_last_post_time AS topic_post_time,
sch_sub_forum_id
FROM
( select * FROM sch_forum_topics WHERE sch_sub_forum_id = 4
ORDER BY topic_last_post_time DESC
LIMIT 0, 15 ) main
INNER JOIN _users
ON ( _users.userid = main.topic_poster )
INNER JOIN _profile
ON _profile.userid = _users.userid
INNER JOIN _class
ON _users.classid = _class.classid
INNER JOIN _level
ON _class.level_id = _level.id
INNER JOIN _unit
ON _class.unitid = _unit.unitid
INNER JOIN _department
ON _unit.deptid = _department.deptid
INNER JOIN _faculty
ON _department.facid = _faculty.facid
INNER JOIN _university
ON _faculty.schid = _university.schid);