sì c'è un modo, ora non ti scrivo il codice perchè non ho un db sottomano quindi rischierei di scriverlo male, ma il concetto è che:

- ordini la query in modo da sapere sicuramente con che regione inizierà lo script
- ti crei una variabile $nome_regione e la setti col nome della prima regione
- apri il primo <optgroup label="nomeprima regione">
- fai una query e cominci a far ciclare
- ad ogni ciclo imposti la variabile $regione_confronto
- con un if controlli se regione_confronto è diverso da $nome_regione; se sì, parti con l'option group, altrimenti è un option value normale
- prima della fine del ciclio ti salvi il nome della regione in $nome_regione.

Dovrà succedere così, supponendo di partire dal Piemonte
$nome_regione = Piemonte
parte il ciclo
$regione_confronto = $row["regione"] (quindi sarà Piemonte)
$regione_confronto è diverso da $nome_regione? in questo caso no, quindi crei un option value
a fine ciclo ti salvi Piemonte in $nome_regione.
Riparte il ciclo, troverà ad esempio Calabria
$regione_confronto = $row["regione"] (quindi sarà Calabria)
$regione_confronto è diverso da $nome_regione? sì, perchè Piemonte != Calabria -> quindi chiudi l'optiongroup e ne apri un altro.

Quando esce dal ciclo, ricordati di chiudere l'optiongroup oltre alla select

sembra lungo, ma è molto veloce come codice