MySQL - Selecting data from three different tables - mysql

I'm trying to select data from three different tables. How would I go about joining these tables to make the proper query?
****customers****
--------------------------
id full_name
54 Matt Damon
53 Jimmy Kimmel
****samples****
--------------------------
id rma_id
57 USARP011315-25
56 USARP011315-24
****early_ships****
--------------------------
customer_id sample_id shipping_carrier
54 57 UPS
53 56 FedEx
This is the query I've been running but I've yielded 0 results. This is the tutorial I've been following: http://javarevisited.blogspot.com/2012/11/how-to-join-three-tables-in-sql-query-mysql-sqlserver.html
SELECT samples.rma_id, customers.full_name, early_ships.shipping_carrier,
FROM customers c JOIN early_ships e ON c.id = e.customer_id
JOIN samples s ON e.sample_id = s.id

You have to use the table aliases in the SELECT clause. Please check this fiddle I created for you. It's working perfectly. http://sqlfiddle.com/#!2/49462/8

Two issues:
There's an extra comma at the end of the SELECT clause.
In the FROM..JOIN clause, you've given nicknames, but then you're trying to use the original names in the SELECT clause.
Try this:
SELECT s.rma_id, c.full_name, e.shipping_carrier
FROM customers c
JOIN early_ships e
ON c.id = e.customer_id
JOIN samples s
ON e.sample_id = s.id
(whitespace added for readability)

Related

How can I add a column to a right join select query

I am trying to find a way to add a country code to a database call record based on a phone number column. I have a table with countries and their dialling codes called countries. I can query all records and add the country code after but I need to be able to filter and paginate the results.
I am working with a system I don't have much control over so adding new columns to tables or rewriting large blocks of code isn't really an option. This is what I have to work with.
Countries Table.
id
name
dialling_code
1
Ireland
353
2
America
1
Call Record table.
id
startdatetime
enddatetime
route_id
phonenumber
duration_seconds
1
2014-12-18 18:51:12
2014-12-18 18:52:12
23
3538700000
60
2
2014-12-18 17:41:02
2014-12-18 17:43:02
43
18700000
120
Routes table.
id
number
enabled
23
1234567890
1
43
0987654321
1
I need to get sum values of duration, total unique phone numbers all grouped by route_id, route_number but now we need to group these results by country_id so we can group callers by country. I use the mysql query below to get sum values of duration, total unique phone numbers all grouped by route_id, route_number. This query was written by another developer a long time ago.
SELECT
phone_number,
route_number,
COUNT(callrecord_id) AS total_calls,
SUM(duration_sec) AS total_duration,
callrecord_join.route_id
FROM routes
RIGHT JOIN (
SELECT
DATE(a.startdatetime) AS call_date,
a.id AS callrecord_id,
a.route_id AS route_id,
a.phonenumber AS phone_number,
a.duration_seconds as duration_sec,
b.inboundnumber AS route_number,
FROM callrecord AS a
INNER JOIN routes AS b ON a.route_id = b.id
WHERE DATE_FORMAT(a.startdatetime, '%Y-%m-%d') >= '2014-12-18'
AND DATE_FORMAT(a.startdatetime, '%Y-%m-%d') <= '2014-12-18'
AND b.isenabled = 1
) AS callrecord_join ON routes.id = callrecord_join.route_id
GROUP BY route_id, route_number
LIMIT 10 offset 0;
I have everything up to adding a country_id in the right join table so I can group by the country_id.
I know I could loop through each country using php and get the results using a where clause, something like the below but I cannot paginate these results or filter them easily.
WHERE LEFT(a.phonenumber, strlen($dialling_code)) = $dialling_code
How can I use the countries table to add a column to the join table query with the country id so I can group by route_id, route_number and country_id? Something like the table below.
id
startdatetime
enddatetime
route_id
phonenumber
duration_seconds
country_id
1
2014-12-18 18:51:12
2014-12-18 18:52:12
23
3538700000
60
1
2
2014-12-18 17:41:02
2014-12-18 17:43:02
43
18700000
120
2
The RIGHT JOIN from routes to callrecord_join serves no purpose, as you already have the INNER JOIN between routes and callrecord in the sub-query, which is on the righthand side of the join.
You can use the join you have described -
JOIN countries c ON LEFT(a.phonenumber, LENGTH(c.dialling_code)) = c.dialling_code
but it will give the same result as:
JOIN countries c ON a.phonenumber LIKE CONCAT(c.dialling_code, '%')
which should be slightly less expensive.
You should test the join to countries to make sure none of your numbers in callrecord join to multiple countries. Some international dialling codes are ambiguous, so it depends on which list of dialling codes you are using.
SELECT a.*, COUNT(*), GROUP_CONCAT(c.dialling_code)
FROM callrecord a
JOIN country c ON a.phonenumber LIKE CONCAT(c.dialling_code, '%')
GROUP BY a.id
HAVING COUNT(*) > 1;
Obviously, you will need to batch the above query if your dataset is very large.
I hope I am not grossly over-simplifying things, but from what I understand of your question the query is just:
SELECT
r.id AS route_id,
r.number AS route_number,
c.name AS country_name,
SUM(a.duration_seconds) AS total_duration,
COUNT(a.id) AS total_calls,
COUNT(DISTINCT a.phonenumber) AS unique_numbers
FROM callrecord AS a
JOIN routes AS r ON a.route_id = r.id
JOIN countries c ON a.phonenumber LIKE CONCAT(c.dialling_code, '%')
WHERE a.startdatetime >= '2014-12-18'
AND a.startdatetime < '2014-12-19'
AND r.isenabled = 1
GROUP BY r.id, r.number, c.name
LIMIT 10 offset 0;
Please note the removal of DATE_FORMAT() from the startdatetime to make these criteria sargable, assuming a suitable index is available.

Mysql Query with two seperate join

Does anyone know the solution to this problem ?
There are 3 Tables: orders, order_groups and stores.
I want to list the orders, with the names of the stores where the order was placed, and where the order is going to be delivered.
I keep the from_store_id, and to_store_id in the order_groups table
Listing these orders would be simple, i just left join the order_groups to orders, and select the name, from_shop_id and to_shop_id, but the problem is i want the name of the stores not the id, and the store names are placed in a different table (stores)
Here is what im talking about:
Table orders
id group_id name madeup_id
1 11 johnny cash 1
2 12 billy bob 1
LEFT JOIN order_groups on order_groups.id = orders.group_id
Table order_groups
id from_store_id to_store_id
11 55 56
12 56 55
Table stores
id store_name
55 thisstore
56 thatstore
The result im looking for is:
name from_store to_store
1.johhny cash thisstore, thatstore
2.billy bob thatstore, thisstore
The statement i have yet:
SELECT
orders.name, something as from_store, something as to_store
FROM orders
LEFT JOIN order_groups on order_groups.id = orders.group_id
somehow join stores on the order_groups.from_store_id = stores.id
WHERE orders.madeup_id = 1
Any idea how to select and join the store names to the query ?
One more question. I actually want to list two kind of orders in one query from different tables too, im on the right track with this structure ?
SELECT a,b FROM a LEFT JOIN b ON b.something=a.something WHERE something
UNION ALL
SELECT a,b FROM c LEFT JOIN c ON c.something=a.something WHERE something
You only need to join 2 times the same table!
SELECT
orders.name, fromStore.store_name as from_store, toStore.store_name as to_store
FROM orders
LEFT JOIN order_groups on order_groups.id = orders.group_id
left join stores fromStore on the order_groups.from_store_id = fromStore.id
left join stores toStore on the order_groups.to_store_id = toStore.id
WHERE orders.madeup_id = 1

Group by with MAX() still selects other row

I'm writing a query where I need to select student name by who has a MAX gradelevel_id. How ever it still selects the other row with the same id of the student where I already define what gradelevel_id should I select.
schoolyear_id | student_id | gradelevel_id
407 18 307
409 18 309`
Query:
SELECT
student_mt.student_id,
registration_mt.firstname, registration_mt.middlename, registration_mt.lastname,
MAX(grade.gwa)
FROM schoolyear_student_lt
INNER JOIN gradelevel_mt ON gradelevel_mt.gradelevel_id = schoolyear_student_lt.gradelevel_id
INNER JOIN student_mt ON student_mt.student_id = schoolyear_student_lt.student_id
INNER JOIN registration_mt ON registration_mt.registration_id = student_mt.registration_id
INNER JOIN student_grade ON student_grade.student_id = schoolyear_student_lt.student_id
INNER JOIN grade ON grade.grade_id = student_grade.grade_id
WHERE gradelevel_mt.gradelevel_id = 309
GROUP BY student_mt.student_id;
If I define 307 in my WHERE CLAUSE still selects the student name which I should not already see in my row.
Output:
student_id | firstname | middlename | lastname | MAX(grade.gwa)
18 Billie Joe Armstrong 88
(This is more of a comment) I guess you accidentally stumbled on the quirky MySQL behavior of GROUP BY.
When using GROUP BY, in the SELECT clause we can only put the GROUP BY predicate (student_mt.student_id) and aggregate functions (MAX(grade.gwa)). Even though MySQL allows this, the DBEngine assumes you know what you are doing but might result in anomalies.
Why not get take the approach of getting the student_id(pk) for the MAX(grade.gwa) as a inner sub query and then do the INNER JOIN's with other tables to select what you wanted in the outer subquery.

SQL search in relations

I have a main table with two relations.
Data structure and example:
A/Employee
id fields
1 Mike Miller
2 Lisa Miller
B/Skill
aid name
1 SQL
1 PHP
C/Language
aid name
1 German
I need a query which shows results from the main table and searches for a keyword in the relation tables.
Search for Miller -> Mike Miller, Lisa Miller
Search for SQL -> Mike Miller
Search for German -> Mike Miller
There are 10.000 rows in the main table and 100.000 relations.
I tried it with JOIN but the query is really slow.
Also the same row from the main table is displayed a view several times when there are more than one relations for this row:
Search for Miller
Returns: Mike Miller, Mike Miller
(Mike Miller displayed more than one time)
SELECT fields
FROM A
JOIN B ON id = B.aid JOIN C ON id = C.aid
WHERE fields LIKE '%"+$search+"%' OR B.name LIKE '%"+$search+"%' OR C.name LIKE '%"+$search+"%'"
I tried to fix the second problem with DISTINCT but now rows without relations are not displayed.
I want to display every row from the main table exactly one time. Which query do I need?
The problem with your first query, as you mention yourself, is that you get several duplicate rows returned. Not strange, since I guess the relation between table A and table B & C is one-to-many.
In your next attempt you added DISTINCT, and that will indeed get rid off the duplicates, but the regular join (or inner join) will only return matches where data can be joined, i.e. where data exists in both joined tables.
Introducing LEFT JOIN:
SELECT DISTINCT fields
FROM A
LEFT JOIN B ON id = B.aid
LEFT JOIN C ON id = C.aid
WHERE fields LIKE '%"+$search+"%' OR B.name LIKE '%"+$search+"%' OR C.name LIKE '%"+$search+"%'"
This will always search all data from table A, and those from table B & C where joins can be made. The DISTINCT will make sure that only unique rows are returned. You could also use GROUP BY for the same result, but that's usually used for aggregate methods.
Use LEFT JOIN.
Example:
SELECT distinct e.empname FROM Employee e
LEFT JOIN skill s ON s.aid = e.id
LEFT JOIN lang l ON l.aid = e.id
WHERE e.empname LIKE '%Miller%' OR s.name LIKE '%Miller%' OR l.name LIKE '%Miller%'
Proof SQL Fiddle

JOINS in the query end up displaying too many rows

Below is my query:
$query = "
SELECT DISTINCT gr.SessionId, t.TeacherUsername, t.TeacherForename,
t.TeacherSurname, cm.ModuleId, m.ModuleName,
cm.CourseId, c.CourseName, st.Year, st.StudentUsername,
st.StudentForename, st.StudentSurname, gr.Mark, gr.Grade
FROM Teacher t
INNER JOIN Session s ON t.TeacherId = s.TeacherId
JOIN Grade_Report gr ON s.SessionId = gr.SessionId
JOIN Student st ON gr.StudentId = st.StudentId
JOIN Course c ON st.CourseId = c.CourseId
JOIN Course_Module cm ON c.CourseId = cm.CourseId
JOIN Module m ON cm.ModuleId = m.ModuleId
WHERE
('".mysql_real_escape_string($sessionid)."' = '' OR gr.SessionId = '".mysql_real_escape_string($sessionid)."')
ORDER BY $orderfield ASC
";
You don't need to worry about the WHERE clause and ORDER BY clause. My problem is that the query result shows 26 rows when it should show 13 rows.
I know the reason for this and it is because the Course_Module table is a cross reference table between Course table and Module table and is needed so that it is able to link Course table and Module table together.
But Course Table uses CourseId to JOIN another table and so does Course_Module Table. So CourseId is used twice in the JOINS section and because of this it is duplicating rows again. So there should be 13 rows but because each row is duplicate it shows 26.
I tried GROUP BY cm.CourseId but it ends up displaying 2 rows which are two different CourseId which is not what I want at all.
So what my question is that is there are way I can use the Course_Module table to JOIN tables but ignore it when it comes to displaying results?
If query was this:
$query = "
SELECT DISTINCT gr.SessionId, t.TeacherUsername, t.TeacherForename,
t.TeacherSurname, cm.ModuleId, m.ModuleName,
cm.CourseId, c.CourseName, st.Year, st.StudentUsername,
st.StudentForename, st.StudentSurname, gr.Mark, gr.Grade
FROM Teacher t
INNER JOIN Session s ON t.TeacherId = s.TeacherId
JOIN Grade_Report gr ON s.SessionId = gr.SessionId
JOIN Student st ON gr.StudentId = st.StudentId
JOIN Course c ON st.CourseId = c.CourseId;
This query shows 13 rows but it means there is no link to Module Table so don't know name of Modules taken for each grade reort.
Below is example of result I am getting at moment:
Student Session Module Course Grade
S1 AAA CHT2520 ICT A
S1 AAA CHT2520 ICT A
S2 AAA CHT2520 ICT B
S2 AAA CHT2520 ICT B
S3 AAB CHT2220 BIT D
S3 AAB CHT2220 BIT D
S4 AAC CHI2250 COMP A
S4 AAC CHI2250 COMP A
It should be:
Below is result I am getting at moment:
Student Session Module Course Grade
S1 AAA CHT2520 ICT A
S2 AAA CHT2520 ICT B
S3 AAB CHT2220 BIT D
S4 AAC CHI2250 COMP A
Thank You
Your select clause asks for 14 columns. The results you showed only had 5. If you limit your select clause to those 5 columns, you'll get the 13 rows that you want.
To include all 14 columns, look at the other columns in the results. Realize that right now, you don't have 26 rows in your result set so much as you have 13 pairs of rows. Look carefully at each pair, and somewhere you'll find a column that's different — something that distinguishes one record in a matched pair from the other. Add a condition to the join on the table that hosts this column to prevent one of the values from making it to your results, and you'll get the right number of rows. This may require a derived table or correlated sub-query in the join condition to limit the join to only the first match (for some definition of "first" determined by the sub query).
I am not a big expert but why are you using two different joins in your case? Stick to INNER JOIN throughout the query and it might fix the issue.