Easiest way to think of it:
With a join I put the data in two tables side-by-side. However, since I determine how the rows match up (table1.col1 = table2.col2 for instance), I can only get the rows that match exactly based on my join criteria (INNER JOIN), all the rows of the first table but only the matching rows of the second (LEFT JOIN or LEFT OUTER JOIN), all the rows of the second table but only the matching rows of the first (RIGHT JOIN or RIGHT OUTER JOIN), or all rows of both table, regardless of match (FULL JOIN or FULL OUTER JOIN). I could also do a CROSS JOIN which takes every row of table1 and matches it with every row of table2. If you remember Punnett squares from Biology with the XX and Xx type of stuff, that's a CROSS JOIN.
A UNION is where I take one table and then I take the second table and put the contents right after it. Like appending to the end of the list. A UNION call by itself will only return distinct records, meaning if there are identical records in Table1 and Table2, only one record will be returned. A UNION ALL says give me all the records, even the duplicate.
A big difference between the two is with a JOIN the tables have to be relatable in some way (with the exception of the cross join). I've got to be able to do a comparison where I can say a row in table1 matches a row in table2. Another difference is with a UNION or UNION all, my queries both have to have the same number of columns. A join can have different numbers of columns on both sides.
JOIN: A - B
K. Brian Kelley
Author: Start to Finish Guide to SQL Server Performance Monitoring
K. Brian Kelley