Twoje założenie jest fałszywe; podzapytanie zostanie wykonane tylko raz. Powodem, dla którego jest to wolniejsze niż łączenie, jest to, że IN nie może korzystać z indeksów; musi skanować swoje argumenty raz za każdym razem, gdy WHERE klauzula jest oceniana, to znaczy raz na wiersz w tabeli A. Możesz zoptymalizować zapytanie bez używania zmiennych lub procedur składowanych, po prostu zastępując IN ze sprzężeniem, w ten sposób:
SELECT tableA.field1, tableA.field2, [...]
FROM tableA
INNER JOIN tableB ON tableA.id = tableB.id
O ile nie masz nic przeciwko odzyskaniu każdego pola z obu tabel, musisz wyliczyć pola, które chcesz w SELECT klauzula; tableA.* na przykład wywoła błąd składni.