Der Optimizer kann nicht umhin, eben den in seinen Augen (bzw. der Gedanken der Entwickler) optimalen Weg zu finden.
Hier nützt es auch nicht, den SQL zu drehen und zu wenden, da der Optimizer das dann eben umbaut.
Wie gesagt, eine temporäre Tabelle gibt es nicht, der jeweilige Join wird immer direkt ausgeführt, also quasi eingebettet.
Aus dem "inner join a ..." wird eben ein "inner join (select .... ) a ...".
Aus dem "inner join b ..." wird dann ein "inner join (select ... inner join (select ...) a) b ...
Somit erklären sich dann eben die Laufzeiten, da doch erhebliche Daten bewegt werden müssen.
Welche QAQQINI-Einstellung dann noch zu diversen anderen Ergebnissen führen kann ist mitunter auch nicht mehr oder nur schwer nachzuvolllziehen.

Ich habe hier auch gerade eine Tabelle mit 44Mio Sätzen.
Die Like-Klausel benötigt in beiden Fälle (mit oder ohne Upper) für den Tablescan ca. 20 Sekunden.

Deinen Weg der Einzelschritte mache ich auch häufig per embedded SQL. Zusätzlich mach ich allerdings auch einen "Create Index" auf die temporäre Tabelle.
Um nicht ständig neue Tabellen zu erfinden kann man i.Ü. auch mit "declare global temporary Table" sitzungsbezogene Arbeitstabellen anlegen.