How does an INNER JOIN decide which rows match, and what happens to rows that find no partner?
An INNER JOIN pairs every row on the left with every row on the right that satisfies the join condition, and keeps only the pairs that matched. Rows on either side with no partner are discarded entirely. That last part is what makes it different from the other join types, and it is also why an INNER JOIN can silently reduce a result set.
The condition is usually an equality on a foreign key, but it can be anything that evaluates to TRUE — a range, a comparison, even an expression. What matters is that NULLs are involved: any comparison with a NULL evaluates to UNKNOWN, never TRUE, so a row whose join key is NULL can never be matched by an equality condition.
Two structural details. The order of the FROM clauses does not change the result — A INNER JOIN B and B INNER JOIN A return the same rows with the columns swapped — but it does change how the optimizer reads the query, and it changes which table has to be the driving table. And the join is a set operation on pairs, so if both sides have duplicate keys, the number of output rows is the product of the matching rows on each side.
The last point is the one to watch. Joining a customer with three orders and then aggregating produces three rows for that customer, and a SUM over those rows counts the customer's order values three times. That fan-out is not a bug in the join; it is the correct output of the join, and it becomes a bug in whatever aggregates it afterwards.