Nie znam dobrze mysql więc zapytania dokładnie nie podam bo nie znam odpowiednika generate_series z postgresa. Ale w skrócie wyglądałoby to tak, że:
- generuje sobie w cte zestaw dat od np 2022-05-13 do 2022-12-31
- w drugim cte wyławiam terminy zajęte
- trzecie cte - select data from cte1 except select data from cte2 - da ci wszystkie wolne terminy
a główne zapytanie - select min(data) from cte3 da Ci oczekiwany wynik.
Dla postgresa zapytanie wyglądało by tak: (tabela cte "zajete" to dane z Twojego przykładu)
with zajete as (
select 1 as id, 1 as m_id, '2022-05-13'::date as od, '2022-05-18'::date as do
union all select 1, 1, '2022-05-19', '2022-05-21'
union all select 1, 1, '2022-07-18', '2022-07-24'
), terminy as (
select data::date from generate_series('2022-05-13', '2022-12-31', interval '1 day') as a (data)
), zajete_dni as (
select distinct terminy.data
from terminy
join zajete on (terminy.data between zajete.od and zajete.do)
), wolne_dni as (
select data
from terminy
except
select data
from zajete_dni
)
select min(data) from wolne_dni