Ty Wigginton hit a three-run homer off Johnson in the first inning as the Astros averted a three-game sweep and won for the ninth time in 11 games. Arizona, which had won three straight, fell into a first-place tie with the Los Angeles Dodgers in the NL West.
The Astros retired Craig Biggio's No. 7 before the game, and the seven-time All-Star waved to fans and touched his heart during a lively ceremony.
Then, Oswalt went to work.
The right-hander, 5-0 in his last seven starts, walked two and matched a season high for strikeouts. He improved to 7-1 in his career against Arizona.
"Roy was just phenomenal," Houston manager Cecil Cooper said. "Without question, this was his best outing of the year. It probably was the second-best outing I've ever seen him throw after the (NLCS) playoff game in 2005.
"You saw two of the best pitchers ever out there. Randy pitched a great game, too. He just made one mistake."
Oswalt (11-8) said he was off his game more than it appeared.
"I got through it," he said. "They were hitting it pretty well but I got them to hit it right at someone."
Oswalt had given up at least five hits in all 23 previous starts this season. But he didn't think Sunday's effort was his best of the year.
"I can't say the best," he said. "It's better results than I've had in a while."
Astros slugger Lance Berkman called it Oswalt's best performance in at least two seasons. But he dispelled the notion that Oswalt was more locked in because he was facing Johnson (10-9).
"I think it was the other way around," Berkman said. "I think Randy was more focused because he was facing Roy. Randy was pretty dominant and Roy was real dominant."
Wigginton provided all the support Oswalt needed with his fourth homer in seven games, following a walk to Mark Loretta and a single by Miguel Tejada. Wigginton extended his hitting streak to 14 games and is hitting .510 (26-for-51) since the streak began on Aug. 3.
Arizona catcher Chris Snyder said Wigginton hit a split-finger fastball from Johnson.
"Wasn't that bad of a pitch," Snyder said. "I mean, any time that guy puts wood on it, it's a base hit right now."
Oswalt gave up a single to Stephen Drew in the third inning. But Snyder, who had walked and been sacrificed to second, tried to score on the hit to shallow right and was thrown out at the plate by Hunter Pence to end the inning.
Oswalt retired 15 straight batters from there before he was taken out after eight. Jose Valverde finished the two-hitter, allowing only Drew's single in the ninth before earning his 31st save in 37 chances.
Arizona manager Bob Melvin said his scouting reports said Oswalt had lost some velocity this season, but he didn't see much drop-off on Sunday.
"That's probably as hard as he's thrown all year," Melvin said. "He was on it today."
Trying for his 295th win, Johnson allowed eight hits and a walk in seven innings. He struck out five but lost for the first time in seven career starts against Houston. The Big Unit entered 4-0 with a 3.60 ERA against the Astros.
After winning the first two games by a combined score of 23-7, the Diamondbacks were shut out for the fifth time this season.
"You kind of knew after the first two games, to see that kind of a performance three times would've been a lot to ask for," Johnson said.
- The game started 19 minutes late because of the ceremony to honor Biggio.
- It was Oswalt's second 10-strikeout performance of the season. The first came against Milwaukee on June 10.
- Houston's 55 errors are the fewest in the major leagues. The Yankees rank second with 61.