Ok. esisteva un errore logico.
questa query però non riesce a prelevare tutti i casi che servono:
facendo una prova per una data come ad es. il sabato 19-09-2026,
prende solo la corsa della tabella giornaliera e solo quella della tabella con frequenza lun-ven-scol ignorando che cmq vanno prese, in questo caso anche quelle con frequenza scolastica che sono valide tutti i giorni feriali invernali compreso il sabato.
Ho fatto anche io diverse prove ma non riuscivo a trovare la soluzione.
Quindi mi son andato a ripassare cosa servono le "union all" perché a sto punto, qualche cosa che non ricordo esattamente e che non ho semplicemente capito ci deve essere.
Con l'aiuto dell'AI, che mi stava mettendo un po fuori strada, a forza di tentativi ho raggiunto quanto segue.
SET @giorno_servizio='2026-09-19';
WITH periodo AS (
SELECT
descrizione_periodo,
CASE descrizione_periodo
WHEN 'periodo_pasquale' THEN 'pasquale'
WHEN 'periodo_natalizio' THEN 'natalizio'
WHEN 'periodo_estivo' THEN 'estivo'
WHEN 'periodo_invernale' THEN 'invernale'
END AS periodo
FROM gbgar_periodi
WHERE @giorno_servizio BETWEEN data_inizio AND data_fine
ORDER BY
CASE descrizione_periodo
WHEN 'periodo_pasquale' THEN 1
WHEN 'periodo_natalizio' THEN 2
WHEN 'periodo_estivo' THEN 3
WHEN 'periodo_invernale' THEN 4
ELSE 99
END
LIMIT 1
),
-- Mappatura dinamica di tutte le frequenze valide per il giorno di servizio
giorno_frequenze AS (
SELECT 'giornaliera' AS frequenza -- Valida sempre, per qualsiasi giorno
UNION ALL
SELECT
CASE
-- SE È FESTIVO (Domenica o Tabella Festività)
WHEN DAYOFWEEK(@giorno_servizio) = 1
OR EXISTS (SELECT 1 FROM gbgar_festivita f WHERE f.GIORNO = @giorno_servizio)
THEN 'festiva'
-- SE È SABATO
WHEN DAYOFWEEK(@giorno_servizio) = 7
THEN 'sabato'
-- SE È LUNEDÌ-VENERDÌ
ELSE 'feriale'
END AS frequenza
UNION ALL
-- Aggiungiamo le frequenze specifiche richieste per il periodo invernale scolastico
SELECT 'scolastica' AS frequenza WHERE DAYOFWEEK(@giorno_servizio) BETWEEN 2 AND 7
UNION ALL
SELECT 'sab-scol' AS frequenza WHERE DAYOFWEEK(@giorno_servizio) = 7
UNION ALL
SELECT 'lun-ven-scol' AS frequenza WHERE DAYOFWEEK(@giorno_servizio) BETWEEN 2 AND 6
UNION ALL
SELECT 'sab-scol' AS frequenza
WHERE DAYOFWEEK(@giorno_servizio) BETWEEN 2 AND 6
)
SELECT
c.ID_CORSA,
tm.TABELLA AS ID_TM,
c.LINEA,
ic.UBICAZIONE AS PARTENZA,
c.ORARIO_CORSA,
fc.UBICAZIONE AS ARRIVO,
c.FINE_CORSA,
c.KM,
p.periodo,
tm.FREQUENZA AS frequenza_tabella
FROM gbgar_corse c
INNER JOIN gbgar_fermate ic ON c.PARTENZA = ic.ID_FERMATA
INNER JOIN gbgar_fermate fc ON c.ARRIVO = fc.ID_FERMATA
INNER JOIN gbgar_tab_marcia tm ON tm.ID_TM = c.ID_TM
CROSS JOIN periodo p
-- Sostituiamo il CROSS JOIN con un INNER JOIN sulla tabella delle frequenze ammesse
INNER JOIN giorno_frequenze gf ON tm.FREQUENZA = gf.frequenza
WHERE
tm.PERIODO = p.periodo
ORDER BY
c.ORARIO_CORSA;
Insomma, chiedo scusa ma non riuscivo a vedere il quadro della situazione.
Quindi poi ho capito che in pratica le union all servono eccome e alla fine il tutto me lo spiego cosi:
WITH: apre una tabella provvisoria che si chiama"periodo" (ho scoperto che sono definite CTE).
e poi AS per istruire cosa deve rappresentare questa CTE, cioè cosa deve contenere.
Al suo interno prelevo i dati da una tabella fisica con una subquery praticamente, una select insomma sulla tabella fisica gbgar_periodi che funge da tabella sorgente in questo blocco.
Prelevo la colonna sorgente 'descrizione_periodo'
Nel tempo stesso dentro la select si pongono 4 casi (assomiglia allo swich di php), corrispondenti ai record della tabella fisica sorgente e pone 4 espressioni relative da inserire nella colonna 'periodi', della CTE aperta.
E' un po' strano il fatto che il nome della colonna 'periodo' in questa CTE, venga assegnato in chiusura
(END AS periodo), ci ho messo un po' a intuire che a livello di sintassi, prima si creano le condizioni per stabilire il valore potenziale e poi si chiude il CASE assegnando il nome alla colonna.
Poi,
Siccome la tabella provvisoria 'periodi' deve prelevare un solo periodo a seconda del valore di @giorno_servizio settato inizialmente (che in php poi sarà rappresentato da $data_servizio),
fa un controllo nella WHERE per prelevare il record sorgente che comprende tra le sue date (data_inizio e data_fine) il valore di @giorno_servizio.
Siccome però il periodo pasquale e natalizio sono inglobate nel periodo compreso tra le date del periodo invernale è necessario ordinare una priorità tra i periodi, e limitare il prelevamento al primo periodo che ha corrispondenza.
Qua si chiude la prima CTE.
Niente UNION ALL qui visto che questa viene agganciata con una CROSS JOIN nella select finale.
Poi viene creata un' altra CTE che si chiama giorno_frequenze.
Prelevando il blocco a se stante, e provandolo in console sql, vedo che questa tabella ha una sola colonna : 'frequenza'.
in questa convogliano i valori di 5 UNION ALL.
E finalmente ho capito cosa si intende per union all. Avevo capito servisse per unire record di tabelle diverse in una sola tabella ma con la clausola che ci fosse compatibilità tra i campi delle tabelle sorgenti, vale a dire stesso numero di colonne e valori compatibili. (int, char ..... quello che è) .
Dal momento che la tabella che raccoglie i dati è una CTE creata ad arte diciamo, per forza che i campi son compatibili, visto che creandola vado a prender i dati che possono essere uniti.
Quindi:
1° valore: 'giornaliera'
che serve per le corse delle tabelle di marcia che nella tabella sql sorgente (gbgar_tab_marcia), hanno quel valore nel campo "frequenza".
poi la prima UNION ALL, e ci metto:
valore: 'festivo', 'sabato', 'feriale'
ricavato dal calcolo della funzione DAYOFWEEK (@giorno_servizio), impostando nella sua SELECT una condizione (CASE) per cui ottengo uno dei tre potenziali valori.
Poi le seguenti UNION ALL per avere tutti i valori possibili da incrociare con periodi:
'scolastica' per i gg da 1 a 7 (lun-sab),
'sab-scol' per i gg =7 (sabato),
'lun-ven-scol' per i gg da 1 a 6 (da lun a ven),
di nuovo 'sab-scol' ma stavolta anche per i gg da 1 a 6.
Quindi ho tutti i valori coincidenti con i valori ammessi da gbgar_tab_marcia.frequenza.
La select finale:
-prende i valori elencati in select
-imposta le 2 join su gbgar_fermate per avere i nomi delle fermate,
-imposta la join con la tabella gbgar_tab_marcia indispensabile per college le corse alle tabelle che vengono selezionate a monte.
-CROSS join con la CTE 'periodi' e INNER JOIN con l'altra CTE 'giorno-frequenze' per collegare ogni voce potenziale delle due CTE.
tutta una giornata per arrivare qua, devo ora integrare il tutto nel file select_corse.php, sostituendo lo script attuale con questa unica grande select.
Aggiorno sulla riuscita finale ma in teoria devo solo inserire la variable $data_servizio come parametro al posto di @giorno_servizio usato per questi test in sql.
Aggiungere la clausola AND EXIST ... per sottrarre alla select finale gli id_corsa già registrati per la stesa data in gbgar_serv_detail. (prima di order by c.ORARIO_CORSA).
inserire il tutto come di consueto in una variabile php ed eseguirla per ottenere la mia lista in $corse_da_svolgere quotidianamente e in coerenza dinamica col tipo di periodo e giorno di riferimento.
Buona giornata e grazie di tutto.
Filippo.