A Hash Join usually (always?) Uses a scan, or at least a range scan. The hash join works by scanning both tables left and right (or the range in the tables) and creating a hash table in memory that contains all the values ββthat are βvisibleβ during the scan.
What happened in your case: QO noticed that it can get all the values ββof column C from a non-clustered index that contains this column (as a key or as an included column). Being a non-clustered index is probably pretty narrow, so the total amount of IOs for scanning the entire non-clustered index is not exaggerating. QO also believes that the system has enough memory to store the hash table in memory. Comparing the cost of this request (scanning a non-clustered index from end to end, say 10,000 pages) with the cost of a nested loop that used queries (say 5,000 probes of 2-3 pages each), the scan won as requiring less IO. Of course, this is mainly speculation on my part, but I'm trying to present the matter from the point of view of QO, and the plan is probably optimal.
The factors that contributed to this particular choice of plan would be:
- a large number of evaluated candidates on the right side of the connection
- presence of a join column in a narrow non-clustered index for the left side
- a lot of RAM
For a large estimate of the number of candidates, a better choice than a hash join is only a merge join, and this requires the input to be pre-edited. If both the left side can offer an access path that guarantees order in the joined column, and the right side has a similar opportunity, then you can complete the merge join, which is the fastest join.
Remus Rusanu
source share