Yeah, I should know this by know. And I do. But occasionally I am so caught up in other things that I just can’t remember the different types and keep them straight. Here are two aids for times like those:
First, the following diagram:

Second, the following script gives a more concrete insight into the various joins.
DECLARE @tableX TABLE ( Xkey INT
, colA VARCHAR(10)
, colB VARCHAR(10)
);
DECLARE @tableY TABLE ( Ykey INT
, colC VARCHAR(10)
, colD VARCHAR(10)
);
INSERT INTO @tableX
VALUES (1, 'Bob', 'in X & Y')
, (2, 'Ted', 'in X only');
INSERT INTO @tableY
VALUES (1, 'Carol', 'in X & Y')
, (3, 'Alice', 'in Y only');
SELECT 'from X' [X]
, [x].*
FROM @tableX [x];
SELECT 'from Y' [Y]
, [y].*
FROM @tableY [y];
SELECT 'from X:' [X (INNER) JOIN Y]
, [x].*
, 'from Y:' [ ]
, [y].*
FROM @tableX [x]
INNER JOIN @tableY [y]
ON [x].[Xkey] = [y].[Ykey];
SELECT 'from X:' [X LEFT (OUTER) JOIN Y]
, [x].*
, 'from Y:' [ ]
, [y].*
FROM @tableX [x]
LEFT OUTER JOIN @tableY [y]
ON [x].[Xkey] = [y].[Ykey];
SELECT 'from X:' [X RIGHT (OUTER) JOIN Y]
, [x].*
, 'from Y:' [ ]
, [y].*
FROM @tableX [x]
RIGHT OUTER JOIN @tableY [y]
ON [x].[Xkey] = [y].[Ykey];
SELECT 'from X:' [X FULL (OUTER) JOIN Y]
, [x].*
, 'from Y:' [ ]
, [y].*
FROM @tableX [x]
FULL OUTER JOIN @tableY [y]
ON [x].[Xkey] = [y].[Ykey];
SELECT 'from X:' [X CROSS JOIN Y]
, [x].*
, 'from Y:' [ ]
, [y].*
FROM @tableX [x]
CROSS JOIN @tableY [y]
ORDER BY [x].[Xkey], [y].[Ykey];
SELECT 'came from X:' AS [X UNION Y]
, [x].[Xkey] AS [Xkey_U_Ykey]
, [x].[colA] AS [colA_U_colC]
, [x].[colB] AS [colB_U_colD]
FROM @tableX [x]
UNION SELECT 'came from Y:' AS [X UNION Y]
, [y].[Ykey] AS [Xkey_U_Ykey]
, [y].[colC] AS [colA_U_colC]
, [y].[colD] AS [colB_U_colD]
FROM @tableY [y];
Executing the above script should give these results:

(note: Select keywords in (parentheses) are optional)
And finally, here is a natural language interpretation:
| SELECT FROM | Give me | Special handling |
|---|---|---|
| X | all the rows in X | |
| Y | all the rows in Y | |
| X JOIN Y X INNER JOIN Y | all the rows from X that can be joined with rows in Y, put together as XY rows | – repeats the X portion for each matching Y found. – skips the X row if no rows in Y match the X row. |
| X LEFT JOIN Y X LEFT OUTER JOIN Y | all the rows from X, with rows from Y that can be joined, put together as XY rows | – each X row may join with multiple Y rows. each combination will be a separate XY with the same X. – shows an XY even if no rows in Y were joined to the X row (Y portion is NULLs). |
| X RIGHT JOIN Y X RIGHT OUTER JOIN Y | all the rows from Y, with the rows in X that can join to them, put together as XY rows | – each Y row may be joined to multiple X rows. each combination will be a separate XY with the same Y. – shows an XY even if no rows in X joined to the Y row (X portion is NULLs). – (note: this is equivalent to Y LEFT JOIN X). |
| X FULL JOIN Y X FULL OUTER JOIN Y | all the rows from X, with the rows in Y that can be joined, and all remaining rows in Y, put together as XY rows | – each X row may join with multiple Y rows. each combination will be a separate XY with the same X. – shows an XY even if no rows in Y were joined to the X row (Y portion is NULLs). – shows an XY even if no rows in X joined to the Y row (X portion is NULLs). |
| X CROSS JOIN Y | each row from X joined with each row from Y, put together as XY rows | – repeats each X for each Y. |
| X UNION Y | all the rows from X, plus all the rows from Y, as though they came from the same X table | – column names default to columns from X. – X and Y must have the same column formats. |