MYSQL - using AND in JOIN versus WHERE clause - mysql

If I have this query:
select * from tableA
left outer join tableB on tableA.id=tableB.id
AND tableB.foo = 1
where tableA.owner=10
I get 29 results, but if I move that AND into the WHERE clause like:
select * from tableA
left outer join tableB on tableA.id=tableB.id
where tableA.owner=10
AND tableB.foo = 1
I then get only 17 results.
I've looked all around and cannot find a definitive guide as to how using the AND differs when you use it in the JOIN versus the WHERE clause. Can anyone explain this to me?
Also, if I do something like AND tableB.foo = NULL in the JOIN all of my tableB.foo fields are NULL in the query results, even if they are not null in the table. Does having the AND in the JOIN clause change that field in the FROM selection before being filtered by the WHERE clause?

All of the criteria for the table you are outer-joining to should be in the JOIN clause (like your first query). Putting it in the WHERE clause (like your second query) implicitly converts the OUTER JOIN to an INNER JOIN.
As for your question about AND tableB.foo = NULL that is not proper MySQL syntax. NULLs require special treatment, using operators like IS NULL. You should use AND tableB.foo IS NULL instead.

An outer join joins just the same as an inner join. With the addition that when there is no match for a record, a dummy record with all columns set to null gets joined (so you still get the row from the first table in your results).
In your first query you are looking for matches in tableB with the same ID and foo = 1. For records in tableA with no such match you still get a result row (with all tableA fields null).
In the second query you are looking for matches in tableB with the same ID. For records in tableA with no such match you still get a result row (with all tableA fields null). Then in your where clause you only keep rows with foo = 1. This dismisses all outer-joined records (because their foo is null) and you are where you would have been with a plain inner join.
So always put all criteria on an outer-joined table in the ON clause. (There is one exception though; an anti join, but you can learn that pattern another time.)

Related

MySQL: Trouble trying to get my SQL statement to work when condition is true but can also accept false

I have the following statement:
SELECT tableA.*, tableB.* FROM tableA, tableB WHERE tableA.userUUID = ? AND tableB.uuid=tableA.tableBUUID
So when a record in table A has a valid tableBUUID and the tableBUUID exists in tableB then the query runs file. But when I removed the tableBUUID from table B without removing it from the record in tableA then the query returns nothing (Which is obviously expected)
I am wondering how I can change the query so that even if the tableBUUID does not match anything, the query will still return what it can even if its nothing/ blank columns from tableB?
Thanks all
You can achieve this via a LEFT JOIN:
SELECT tableA.*, tableB.*
FROM tableA
LEFT JOIN tableB ON tableB.uuid = tableA.tableBUUID
WHERE tableA.userUUID = ?
Fiddle.
Performing a LEFT JOIN will return data from tableA and matching records from tableB if it exists in tableB, otherwise it'll return only the records from tableA.
In the syntax you posted above in your question, you're essentially doing a INNER JOIN by comma separating your tables. This is the old syntax, moving forward you should explicitly write INNER JOIN like so:
SELECT * FROM tableA a INNER JOIN tableB b ON a.column_key = b.column_key
Read more about it in the following previous question:
What's the difference between comma separated joins and join on syntax in MySQL?

How to join a derived table

I have a complex query which results in a table which includes a time column. There are always two rows with the same time:
The result also contains a value column. The value of two rows with the same time is always different.
I now want to extend the query to join the rows with the same time together. So my thought was to join the derived table like this:
SELECT A.time, A.value AS valueA, B.value as valueB FROM
(
OLD_QUERY
) AS A INNER JOIN A AS B ON
A.time=B.time AND
A.value <> B.value;
However, the JOIN A AS B part of the query does not work. A is not recognized as the derived table. MySQL is searching for a table A in the database and does not find it.
So the question is: How can I join a derived table?
You cannot join a single reference to a table (or subquery) to itself; a subquery must be repeated.
Example: You cannot even do
SELECT A.* FROM sometable AS A INNER JOIN A ...
The A after the INNER JOIN is invalid unless you actually have a real table called A.
You can insert the subquery's results into another table, and use that; but it cannot be a true TEMPORARY table, as those cannot be joined to themselves or referenced twice at all in almost any query. _By referenced twice, I mean joined, unioned, used as an "WHERE IN" subquery when it is already referenced in the FROM.
If nothing else distinguishes the rows, you can just use aggregation to get the two values:
select time, min(value), max(value)
from (<your query here>) a
group by time;
In MySQL 8+, you can use a cte:
with a as (
<your query here>
)
select a1.time, a1.value, a2.value
from a a1 join
a a2
on a1.time = a2.time and a1.value <> a2.value;

Condition on Left Join

I am trying the below queries where TABLE B is empty AND records in TABLEA
--This query fetched no records
SELECT TABLEA.COLA,TABLEA.COLB FROM TABLEA
LEFT JOIN TABLEB
ON TABLEA.ID=TABLEB.ID
WHERE TABLEB.COL1<>'XYZ'
--This query fetched records .
SELECT COL1 FROM
(
SELECT TABLEA.COLA,TABLEA.COLB FROM TABLEA
LEFT JOIN TABLEB
ON TABLEA.ID=TABLEB.ID
)A WHERE COL1 <>'XYZ'
Could you help me why first query didnt return any records though they look same. My understanding of first query is "I did a left join so if records doesnt exist in tableb, it should be replaced with NULL values. As NULL <>'xyz' all records should be fetched right..
Placing a WHERE condition on the OUTER joined table of an OUTER JOIN effectively renders that join as an INNER JOIN. So, if there are no rows in the outer table which satisfy the condition, then no rows will be returned.
The solution then is to include any such conditions within the join itself. In the example above this is as simple as changing WHERE to AND.
The one condition that must be placed in the WHERE clause is the test for NULL, the so-called exclusion join - I.e. when you actually want to return the inverse set.

What happend first in mysql: join or where

Let's say I have two tables A and B and the following query:
select *
from A
inner join B on A.id = B.id
Where A.id = 5
Does mysql first performs the join or the where?
Edit:
Cause if for example A contains 1000 rows, after the where condition it'll contain only 1 row.
Performing join on a 1 row table is much more efficient so it seems like performing the where first and only then the join is more efficient.
The join happens before the where, however...
The where clause is a filter for all rows returned by the join, but the optimizer will recognise that if an index exists on A.id, it will be used to retrieve rows from A that match, then the join will happen, then theoretically the where clause will filter the results, but again the optimizer will recognise that the condition will already be met so it will skip it as a filter.
All that said, the optimizer will always return the same result as would be returned without the optimizer.

SQL outer join which omits common records

I'm trying to formulate an SQL FULL OUTER JOIN, which includes all values in table A and table B, but not those values common between them.
I have searched the internet, and stumbled upon the following SQL code:
SELECT * FROM TableA
FULL OUTER JOIN TableB
ON TableA.name = TableB.name
WHERE TableA.id IS null
OR TableB.id IS null
Which can be illustrated like so:
I'm not sure I understand the IS null parts. Could the SQL be carried out by simply stating something like the following as a WHERE condition? :
WHERE TableA.id <> TableB.id
What is it you don't understand about the IS NULL clauses?
In an OUTER JOIN (LEFT, RIGHT, FULL) there's a chance that columns from the outer table could end up as NULL.
The clauses
WHERE TableA.id IS null
OR TableB.id IS null
are simply saying that one of the IDs has to be NULL, I.E. if you have a row from TableA there can't exist a matching row from TableB and vice versa.
SELECT Name, ID FROM TableA UNION SELECT Name, ID FROM TableB
EXCEPT
SELECT Name, ID FROM TableB INTERSECT SELECT Name, ID FROM TableA
The first select gets all rows from table A and table B and combines this into 1 result set.
The second select selects all rows that are common between the two.
What the except does is select all rows from the first select - all rows from the second select.
What you end up with is all rows - the rows that are common between the two tables.