When to use LEFT JOIN and when to use INNER JOIN? - mysql

I feel like I was always taught to use LEFT JOINs and I often see them mixed with INNERs to accomplish the same type of query throughout several pieces of code that are supposed to do the same thing on different pages. Here goes:
SELECT ac.reac, pt.pt_name, soc.soc_name, pt.pt_soc_code
FROM
AECounts ac
INNER JOIN 1_low_level_term llt on ac.reac = llt.llt_name
LEFT JOIN 1_pref_term pt ON llt.pt_code = pt.pt_code
LEFT JOIN 1_soc_term soc ON pt.pt_soc_code = soc.soc_code
LIMIT 100,10000
Thats one I am working on:
I see a lot like:
SELECT COUNT(DISTINCT p.`case`) as count
FROM FDA_CaseReports cr
INNER JOIN ae_indi i ON i.isr = cr.isr
LEFT JOIN ae_case_profile p ON cr.isr = p.isr
This seems like the LEFT may as well be INNER is there any catch?

Is there any catch? Yes there is -- left joins are a form of outer join, while inner joins are a form of, well, inner join.
Here's examples that show the difference. We'll start with the base data:
mysql> select * from j1;
+----+------------+
| id | thing |
+----+------------+
| 1 | hi |
| 2 | hello |
| 3 | guten tag |
| 4 | ciao |
| 5 | buongiorno |
+----+------------+
mysql> select * from j2;
+----+-----------+
| id | thing |
+----+-----------+
| 1 | bye |
| 3 | tschau |
| 4 | au revoir |
| 6 | so long |
| 7 | tschuessi |
+----+-----------+
And here we'll see the difference between an inner join and a left join:
mysql> select * from j1 inner join j2 on j1.id = j2.id;
+----+-----------+----+-----------+
| id | thing | id | thing |
+----+-----------+----+-----------+
| 1 | hi | 1 | bye |
| 3 | guten tag | 3 | tschau |
| 4 | ciao | 4 | au revoir |
+----+-----------+----+-----------+
Hmm, 3 rows.
mysql> select * from j1 left join j2 on j1.id = j2.id;
+----+------------+------+-----------+
| id | thing | id | thing |
+----+------------+------+-----------+
| 1 | hi | 1 | bye |
| 2 | hello | NULL | NULL |
| 3 | guten tag | 3 | tschau |
| 4 | ciao | 4 | au revoir |
| 5 | buongiorno | NULL | NULL |
+----+------------+------+-----------+
Wow, 5 rows! What happened?
Outer joins such as left join preserve rows that don't match -- so rows with id 2 and 5 are preserved by the left join query. The remaining columns are filled in with NULL.
In other words, left and inner joins are not interchangeable.

Here's a rough answer, that is sort of how I think about joins. Hoping this will be more helpful than a very precise answer due to the aforementioned math issues... ;-)
Inner joins narrow down the set of rows returns. Outer joins (left or right) don't change number of rows returned, but just "pick up" additional columns if possible.
In your first example, the result will be rows from AECounts that match the conditions specified to the 1_low_level_term table. Then for those rows, it tries to join to 1_pref_term and 1_soc_term. But if there's no match, the rows remain and the joined in columns are null.

An INNER JOIN will only return the rows where there are matching values in both tables, whereas a LEFT JOIN will return ALL the rows from the LEFT table even if there is no matching row in the RIGHT table
A quick example
TableA
ID Value
1 TableA.Value1
2 TableA.Value2
3 TableA.Value3
TableB
ID Value
2 TableB.ValueB
3 TableB.ValueC
An INNER JOIN produces:
SELECT a.ID,a.Value,b.ID,b.Value
FROM TableA a INNER JOIN TableB b ON b.ID = a.ID
a.ID a.Value b.ID b.Value
2 TableA.Value2 2 TableB.ValueB
3 TableA.Value3 3 TableB.ValueC
A LEFT JOIN produces:
SELECT a.ID,a.Value,b.ID,b.Value
FROM TableA a LEFT JOIN TableB b ON b.ID = a.ID
a.ID a.Value b.ID b.Value
1 TableA.Value1 NULL NULL
2 TableA.Value2 2 TableB.ValueB
3 TableA.Value3 3 TableB.ValueC
As you can see, the LEFT JOIN includes the row from TableA where ID = 1 even though there's no matching row in TableB where ID = 1, whereas the INNER JOIN excludes the row specifically because there's no matching row in TableB
HTH

Use an inner join when you want only the results that appear in both tables that matches the Join condition.
Use a left join when you want all the results from Table A, but if Table B has data relevant to some of Table A's records, then you also want to use that data in the same query.
Use a full join when you want all the results from both Tables.

For newbies, because it helped me when I was one: an INNER JOIN is always a subset of a LEFT or RIGHT JOIN, and all of these are always subsets of a FULL JOIN. It helped me understand the basic idea.

Related

Mysql select rows with same id's (3 tables)

I have the following tables:
'blog_content'
'blog_media'
'blog_media_content'
| blog_id | media_id |
========================
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 3 | 4 |
I want to select all blog_media.uri's where blog_media.media_id equals blog_media_content.blog_id.
Please help me to achieve my aim.
An inner join between blog_media and blog_media_content tables would suffice.
SELECT
bm.uri
FROM blog_media bm
INNER JOIN blog_media_content bmc ON bm.media_id = bmc.media_id
WHERE bmc.blog_id =3;
Note:
If you need any additional information from blog table then you need an additional inner join like below:
...INNER JOIN blog_table b ON bmc.blog_id = b.blog_id...
EDIT:
In order to get records for all blog_ids :
SELECT
bm.uri
FROM blog_media bm
INNER JOIN blog_media_content bmc ON bm.media_id = bmc.media_id
ORDER BY bmc.blog_id;

Select data from another table if exists, if not display null

I have two tables.
Invoices
ID | Amount
-----------
1 | 123.54
2 | 553.46
3 | 431.34
4 | 321.31
5 | 983.12
Credit Memos
ID | invoice_ID | Amount
------------------------
1 | 3 | 25.50
2 | 95 | 65.69
3 | 51 | 42.50
I want to get a result set like this out of those two tables
ID | Amount | Cr_memo
---------------------
1 | 123.54 |
2 | 553.46 |
3 | 431.34 | 25.50
4 | 321.31 |
5 | 983.12 |
I've been messing with joins and whatnot all morning with no real luck.
Here is the last query I tried, which pulled everything from the Credit Memo table...
SELECT A.ID, A.Amount FROM Invoices AS A
LEFT JOIN Credit_Memos AS B ON A.ID = B.invoice_ID
Any help or pointers are appreciated.
Your query would work fine. Just add Credit_memo.Amount with an alias:
SELECT Inv.ID,Inv.Amount,IFNULL(C.Amount,'') AS Cr_memo
FROM Invoices Inv LEFT JOIN
Credit_Memos C ON Inv.ID=C.invoice_ID
Result:
ID AMOUNT CR_MEMO
1 124
2 553
3 431 25.50
4 321
5 983
See result in SQL FIDDLE.
You almost got the answer Left Outer Join is what you need but you missed to select Cr_memo from Credit_Memos table. Since you don't want to show Null values when there is no Invoices_ID in Credit Memos table use IFNULL to make NULL's as Empty string
SELECT A.ID, A.Amount, IFNULL(B.Cr_memo,'') AS Cr_memo
FROM Invoices AS A
LEFT JOIN Credit_Memos AS B
ON A.ID = B.invoice_ID
The LEFT JOIN keyword returns all rows from the left table (table1), with the matching rows in the right table (table2). The result is NULL in the right side when there is no match.
SELECT A.ID, A.Amount, IFNULL(B.amount,0) AS Cr_memo FROM Invoices AS A
LEFT JOIN Credit_Memos AS B ON A.ID = B.invoice_ID
here is some useful link about left join link and another

MySQL UNION Only Includes First Table

I need to perform a FULL OUTER JOIN on two tables and I'm trying to implement it in MySQL using the LEFT JOIN/RIGHT JOIN/UNION ALL technique.
Here are the original tables:
giving_totals:
+--------------+---------------+-------------+
| country_iso2 | total_given | supersector |
+--------------+---------------+-------------+
| AE | 1396986989.02 | 3 |
| AE | 596757809.20 | 4 |
| AE | 551810209.87 | 5 |
| AE | 25898255.77 | 7 |
| AE | 32817.63 | 9 |
...
+--------------+---------------+-------------+
receiving_totals:
+--------------+----------------+-------------+
| country_iso2 | total_received | supersector |
+--------------+----------------+-------------+
| AE | 34759000.00 | 3 |
| AE | 148793.82 | 7 |
| AE | 734.30 | 9 |
| AF | 6594479965.85 | 1 |
| AF | 2559712971.26 | 2 |
+--------------+----------------+-------------+
I want the resulting table to have one entry for each country for each supersector code even if it did not give or receive money for that sector (this is from the AidData project dataset in case anyone is familiar.) I thought to accomplish this by doing a UNION of a LEFT JOIN (to get all giving entries) and RIGHT JOIN (to get all receiving entries.) Here's the query I tried:
SELECT g.country_iso2 AS country_iso2, g.total_given AS `total_given`,R.total_received AS `total_received`,g.supersector AS `supersector`
FROM (`giving_totals` `g`
LEFT JOIN `receiving_totals` `r`
ON(((g.country_iso2 = r.country_iso2)
AND (g.supersector = r.supersector))))
UNION ALL
SELECT g.country_iso2 AS country_iso2, g.total_given AS `total_given`,R.total_received AS `total_received`,g.supersector AS `supersector`
FROM (`giving_totals` `g`
RIGHT JOIN `receiving_totals` `r`
ON(((g.country_iso2 = r.country_iso2)
AND (g.supersector = r.supersector))))
But this only returns the first join, whether or not I put the right or left join first. I think I may be misunderstanding the UNION operation because the individual joins each return what I expected. Any help is appreciated as always.
Here is an alternative method to do a full outer join:
SELECT driver.country_iso2 AS country_iso2,
g.total_given AS `total_given`,
R.total_received AS `total_received`,
driver.supersector AS `supersector`
from ((select distinct country_iso2, supersector
from giving_totals
) union
(select distinct country_iso2, supersector
from receiving_totals
)
) driver left outer join
giving_totals gt
on gt.country_iso2 = driver.country_iso2 and
gt.supersector = driver.country_iso2 left outer join
receiving_totals rt
on rt.country_iso2 = driver.country_iso2 and
rt.supersector = driver.country_iso2
That is, do the union as a subquery to get all the combinations you are interested in. Then you can do a left outer join to that table.
The reason for your problem is that aliases in the second query. You can try this instead:
SELECT r.country_iso2 AS country_iso2, g.total_given AS `total_given`,R.total_received AS `total_received`,r.supersector AS `supersector`
FROM (`giving_totals` `g`
RIGHT JOIN `receiving_totals` `r`
ON(((g.country_iso2 = r.country_iso2)
AND (g.supersector = r.supersector))))
The original form would have NULLs for these values.

Mysql joins clarifications

SELECT * FROM left_table OUTER JOIN right_table on left_table.name = righ
t_table.name;
SELECT * FROM left_table FULL OUTER JOIN right_table on left_table.name =
right_table.name;
These 2 statements gives below error. Are they not valid mysql statements ?
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use near 'OUTER
JOIN right_table on left_table.name = right_table.name' at line 1
mysql> SELECT * FROM left_table RIGHT OUTER JOIN right_table on left_table.name
= right_table.name;
and
mysql> SELECT * FROM left_table RIGHT JOIN right_table on left_table.name
= right_table.name;
give same result. So whats the basic use of adding OUTER in statement?
I want to know what special purpose does it serve by adding OUTER in statement where as without adding it also its returning same output.
---- Update ----
Ok i give my table structure
mysql> select * from left_table;
+----+---------+
| id | name |
+----+---------+
| 1 | Pirate |
| 2 | money |
| 5 | Ninja |
| 6 | pradeep |
+----+---------+
and
mysql> select * from right_table;
+----+-------------+
| id | name |
+----+-------------+
| 1 | Rutabaga |
| 2 | Pirate |
| 3 | Darth Vader |
| 4 | Ninja |
+----+-------------+
I ran statements like
mysql> SELECT * FROM left_table LEFT JOIN right_table on left_table.name = right
_table.name where right_table.id is NULL;
+----+---------+------+------+
| id | name | id | name |
+----+---------+------+------+
| 2 | money | NULL | NULL |
| 6 | pradeep | NULL | NULL |
+----+---------+------+------+
and
mysql> SELECT * FROM left_table LEFT OUTER JOIN right_table on left_table.name =
right_table.name where right_table.id is NULL;
+----+---------+------+------+
| id | name | id | name |
+----+---------+------+------+
| 2 | money | NULL | NULL |
| 6 | pradeep | NULL | NULL |
+----+---------+------+------+
They still produce the same output. SO if any1 can show some statement where the results will differ. i would be able to understand it.
I dont know which version of mySql you are using but outer joins are used in combination with left or right, here is the syntax
table_reference NATURAL [{LEFT|RIGHT} [OUTER]] JOIN table_factor
so outer join has no meaning in itself unless its accompanied by left or right.
You may find this reference handy
http://dev.mysql.com/doc/refman/5.0/en/join.html
They may, in your specific testing case, produce the same results. However, they will not always.
An outer join, instead of an inner join, means still show results from the main table (which depends on whether you're using left or right outer join) even if there is no corresponding row in the secondary table.
An inner join would not show the result at all if there was a row in the main table that matched the WHERE conditions but did not have a row that it could join with in the secondary table.
If your tables that you're joining in testing have matching rows for every result (i.e. every row in left_table has a row in right_table with the same value in the name field), then you won't see any difference.
What would make a difference is if you had some rows in one table, or in each table, that didn't have a row in the other table.
For:
select * from left_table left outer join right_table on...where... and there are some rows in the right_table that meet the conditionals (in the where) but don't have a row in the left_table, they would not show. But if there were rows in the left_table that meet the conditionals without a joining row in the right_table, they would still show.
Same goes reverse for RIGHT OUTER JOIN.
EDIT:
LEFT JOIN and LEFT OUTER JOIN are aliases of each other. They are the same thing. I would just use OUTER to clarify for your own sake, if you'd like. I prefer to always use OUTER or INNER just so I can look quickly and see what I did.

Can I specify the order in which the joins occur?

Say I have three tables, A, B and C. Conceptually A (optionally) has one B, and B (always) has one C.
Table A:
a_id
... other stuff
Table B:
a_fk_id (foreign key to A.a_id, unique, primary, not null)
c_fk_id (foreign key to C.c_id, not null)
... other stuff
Table C:
c_id
... other stuff
I want to select All records from A as well as their associated records from B and C if present. However, the B and C data must only occur in the result if both B and C are present.
I feel like I want to do:
SELECT *
FROM
A
LEFT JOIN B on A.a_id=B.a_fk_id
INNER JOIN C on B.c_fk_id=C.c_id
But Joins seem to be left associative (the first join happens before the second join), so this will not give records from A that don't have an entry in C.
AFAICT I must use sub queries, something along the lines of:
SELECT *
FROM
A
LEFT JOIN (
SELECT * FROM B INNER JOIN C ON B.c_fk_id=C.c_id
) as tmp ON A.id = tmp.a_fk_id
but once I have a couple of such relationships in a query (in reality I may have two or three nested), I'm worried both about code complexity and about the query optimizer.
Is there a way for me to specify the join order, other than this subquery method?
Thanks.
In SQL Server you can do
SELECT *
FROM a
LEFT JOIN b
INNER JOIN c
ON b.c_fk_id = c.c_id
ON a.id = b.a_fk_id
The position of the ON clause means that the LEFT JOIN on b logically happens last. As far as I know this is standard (claimed to be ANSI prescribed here) but I'm sure the downvotes will notify me if it doesn't work in MySQL!
Edit: And that's what I get for talking faster than I think. My previous solution doesn't work because 'c' hasn't been joined yet. Let's try this again.
We can use a WHERE clause to limit the results to only those that match the criteria you're looking for, where C has a valid (IS NOT NULL) or B does not have a value (IS NULL). Like this:
SELECT *
FROM a
LEFT JOIN b ON (b.a = a.a)
LEFT JOIN c ON (c.b = b.b)
WHERE (c.c IS NOT NULL OR b.b IS NULL);
Without WHERE Results:
mysql> SELECT * FROM a LEFT JOIN b ON (b.a = a.a) LEFT JOIN c ON (c.b = b.b);
+------+------+------+------+------+
| a | a | b | c | b |
+------+------+------+------+------+
| 1 | 1 | 1 | 1 | 1 |
| 1 | 1 | 2 | NULL | NULL |
| 2 | 2 | 3 | 2 | 3 |
| 3 | NULL | NULL | NULL | NULL |
| 4 | NULL | NULL | NULL | NULL |
+------+------+------+------+------+
With WHERE Results:
mysql> SELECT * FROM a LEFT JOIN b ON (b.a = a.a) LEFT JOIN c ON (c.b = b.b) WHERE (c.c IS NOT NULL OR b.b IS NULL);
+------+------+------+------+------+
| a | a | b | c | b |
+------+------+------+------+------+
| 1 | 1 | 1 | 1 | 1 |
| 2 | 2 | 3 | 2 | 3 |
| 3 | NULL | NULL | NULL | NULL |
| 4 | NULL | NULL | NULL | NULL |
+------+------+------+------+------+
Yes, you use the STRAIGHT_JOIN for this.
When using this keyword the join will occur in the exact order that you specify.
See: http://dev.mysql.com/doc/refman/5.5/en/join.html
Well, I thought up another solution as well, and I'm posting it for completeness (Though I'm actually using Martin's answer).
Use a RIGHT JOIN:
SELECT
*
FROM
b
INNER JOIN c ON b.c_fk_id = c.c_id
RIGHT JOIN a ON a.id = b.a_fk_id
I'm pretty sure every piece I've read about JOINS said that RIGHT JOINs were pointless, but there you are.