The Proc SQL solution is better and more elegant. I hadn't thought through the problem enough to realize that the implicit cartesian join was actually desirable in this case.
To do the loop, you need to read about the set command and the dataset options.
You can use multiple sets in a data step, so you would place one inside the loop, and one outside the loop. The one inside the loop would have to have the appropriate options set to capture the end of the file (end=eof). I'm not sure how to reset back to the beginning. You may need to use random access, requesting the observation specifically by number.
But, I wouldn't waste any time on the looping methodology.