Sottrazione tra array

di pierpippo il
22 risposte

22 Risposte - Pagina 2

  • Ti avevo detto di procedere per gradi...

    24/05/2026 - pierpippo ha scritto:

    Non so come gestire il festivo Pasquale e relativo Lunedì di Pasqua.  

    Pasqua=easter_date(annodaconsiderare)

    Per la pasqua

    Pasquetta=pasqua+1

    Procedi per gradi.

    Spiega cosa devi realizzare.

    Hai visto la query del link?

    Hai due tabelle una si riempie e l'altra si svuota. Un solo comando inserisci i valori nella.m-a-m esi svuota l'elenco delle disponibilità mentre si riempie quella delle assegnazioni.

    Cancelli la voce assegnata (cancelli in realtà il record della m-a-m) e si svuota la griglia "assegnati" mentre si ripopola.la.griglia "da assegnare" semplicemente refreshando le.query.

    Puoi anche operare per numero disponibile cioè un autista è disponibile fino a n' corse o dalle alle... Scalando le ore o le corse. Questo viene stabilito a monte.

    Comincia assegnando un calendario dei festivi, dei periodi, e dei sabato ecc...

    Sai che in que giorni fai un tot di corse.

    Gli altri giorni hai tot + n' corse.

    Lo stesso schema (più o meno) lo fai con i turni degli autisti e devi solo attingere alle corse da assegnare, scalando quelle assegnate, poi attribuisci alla corsa calendarizzata l'autista disponibile. La corsa avrà una partenza e un arrivo, un numero di corse e un orario di rimessa.

    Così con quello che mostri a singhiozzo posso solo dare risposte che nella mia idea di gestione è corretta ma nella tua (magari più informata della mia) risulta errata.

    Per capirci, io ho fatto qualcosa del genere come volontariato per la gestione dei servizi di trasporto emodializzati tra percorsi, autisti e orari. Ma li ci sono delle varianti che non sto a raccontare.

  • 08/06/2026 - sihsandrea ha scritto:

    Hai due tabelle una si riempie e l'altra si svuota. Un solo comando inserisci i valori nella.m-a-m esi svuota l'elenco delle disponibilità mentre si riempie quella delle assegnazioni.

    Cancelli la voce assegnata (cancelli in realtà il record della m-a-m) e si svuota la griglia "assegnati" mentre si ripopola.la.griglia "da assegnare" semplicemente refreshando le.query.

    Alt non sto capendo più nulla :(. Cosa significa m-a-m ? 

    Io non ho capito perchè deve vuotarsi una tabella mentre l'altra si riempie.

    Non deve vuotarsi mica nulla 

    ho una tabella dalla quale vengono presi dati con varie condizioni una delle quali che essi non siano gia presenti un un'altra tabella:

    Ho tre tabelle in gioco:

    • servizi_master      con chiave primaria id_master
    • servizi_detail        con chiave primaria id_detail due chiavi esterne id_master e id_corsa 
    • corse                     con chiave primaria id_corse.

    Le tebella servizi_master e  servizi_detail unite in left join sono i miei turni. 

    Il servizio può essere descritto con un campo aperto, oppure se si tratta di servizi ripetitivi (corse fisse), viene inserito nella tabella _detail solo l'id_corsa.

    Ovvio che la select che preleva i valori dalle tabelle per mostrarle in una pagina web ha una doppia left join come quella che riporto:

    $select_sql="SELECT
    		gbgar_serv_master.id_master,
    		id,
    		autista,
    		stato,
    		data,
    		tipologia,
    		macchina,
    		servizi_da_svolgere,
    		linea,
    		partenza,
    		DATE_FORMAT(orario_corsa,'%H:%i') AS orario_corsa,
    		note
    	FROM gbgar_serv_master
    	LEFT JOIN gbgar_serv_detail
    		ON gbgar_serv_master.id_master=gbgar_serv_detail.id_master	
    	LEFT JOIN gbgar_corse
    	ON gbgar_serv_detail.id_corsa=gbgar_corse.id_corsa	
    	WHERE
    		data between DATE_ADD(CURDATE(), INTERVAL -90 DAY)
    		and DATE_ADD(CURDATE(), INTERVAL 3 DAY)
    		ORDER BY gbgar_serv_master.id_master, gbgar_serv_detail.id;";

    Ma il mio obiettivo è un altro, perché questa cosa qua funziona anche se migliorabile ma per ora va benone.

    Come già ho descritto i dati registrati in _master e _detail arrivano da un form.
    (metto solo 'underscore' senza mettere tutte le volte il prefisso nei nome tabella).

    Se in esso, nella tendina (la chiamo cosi per nn fare casino, e chiamo select le relative query sql), che si chiama "tipologia" , viene selezionato TPL,  grazie alla mia infarinatura di js, appare una tendina dove per option ho i valori prelevati dalla tabella _corse.

    La select che mi preleva i dati delle corse, se non ho capito male potrebbe avere, oltre alle condizioni derivate dal periodo, giorno etc... una condizione (AND NOT EXISTS...) dove gli dico di escludere le voci, i cui id_corsa appaiono già in _detail.

    La mia difficoltà sta nel fatto che pure dentro la subquery ho bisogno di una condizione che deriva dal giorno preso in considerazione che è registrato nel campo DATA facente parte della tabella  _master.

    Cioè se la data relativa all'inserimento dei servizi è la medesima nella tendina di mano in mano non dovrei più trovare i dati corrispondenti agli id_corsa già inseriti. Se la data è un'altra le ricorrenze nella tendina dovrebbero riapparire tutte.

    Per es ho provato a fare cosi:

    MariaDB [gbgarage_test]> SELECT id_corsa,linea,partenza,DATE_FORMAT(orario_corsa,'%H:%i') AS orario_corsa  
    FROM gbgar_corse 
    WHERE periodo='invernale' 
    AND frequenza='scolastica' 
    AND NOT EXISTS (SELECT * FROM gbgar_serv_master LEFT JOIN gbgar_serv_detail ON gbgar_serv_master.id_master=gbgar_serv_detail.id_master 
    LEFT JOIN gbgar_corse ON gbgar_serv_detail.id_corsa=gbgar_corse.id_corsa
    WHERE data='2026-06-06');
    Empty set (0,001 sec)
    

    dal momento che la tabella _detail contiene già due record con (id_corsa 1 e 2),

    in teoria la select che punta alla tabella _corse avrebbe dovuto non mostrare quei due id ma gli altri si e invece mi da Empty.

    Allora non capisco cosa non ho scritto a dovere.

    quindi non so bene come scrivere questa query ma in sostanza devo solo ottenere che la select che estrae i dati dalla tabella _corse non estragga quelle che appaiono nella tabella _detail.

    Ma penso che una volta sistemata ho risolto.

    Buona serata .

  • Intanto m a m significa una relazione molti a molti.

    In soldoni, sono due relazioni uno a molti in due sensi di lettura.

    in pratica hai due relazioni:

    prima relazione 1 a M con tabellaA (lato 1) a TabellaB (lato Molti) 

    seconda relazione 1 a M con tabellaB (lato 1) a TabellaA (lato Molti)

    considerando che ina corsa può essere svolta da più autisti e che ogni autista può effettuare più corse la tua relazione è M a M.

    Vuoi sapere quali dati della tabella di partenza (tabellaA) sono assegnate e quindi presenti nella relazione (tabellaC)

    Vuoi sapere quali dati della Tabella di partenza NON sono state assegnate

    puoi procedere in 2 modi: Con NOT EXISTS oppure con LEFT JOIN ... IS NULL

    Supponiamo di avere:

    Tabella A
    id_a    | nome
    ---------------
    1    | Rossi
    2    | Verdi
    Tabella B
    id_b    | descrizione
    ---------------------
    10    | X
    20    | Y
    30    | Z
    40    | W
    Tabella C relazione molti-a-molti (m a m) tra A e B
    id_a    | id_b
    --------------
    1    | 10
    1    | 20
    2    | 30

    In questo caso il valore 40 della tabella B non è presente nella relazione C.

    caso 1. Con NOT EXISTS

    SELECT b.*
    FROM B b
    WHERE NOT EXISTS (
       SELECT 1
       FROM C c
       WHERE c.id_b = b.id_b
    );
    Risultato
    id_b    | descrizione
    ---------------------
    40    | W

    Come funziona

    Per ogni riga di B, SQL verifica se esiste almeno una riga in C con lo stesso id_b.

    10 >> esiste in C >> escluso
    20 >> esiste in C >> escluso
    30 >> esiste in C >> escluso
    40 >> non esiste >> restituito

    caso 2. Con LEFT JOIN ... IS NULL

    Spesso si usa IS NULL (fai attenzione: non NOT NULL) per trovare i record non associati.

    SELECT b.*
    FROM B b
    LEFT JOIN C c
          ON c.id_b = b.id_b
    WHERE c.id_b IS NULL;
    Risultato
    id_b    | descrizione
    ---------------------
    40    | W

    Come funziona

    La LEFT JOIN mantiene tutte le righe di B:

    b.id_b    | c.id_b
    ----------------
    10    | 10
    20    | 20
    30    | 30
    40    | NULL

    già dal risultato si capisce che fino al id_b 30 hai la lista delle corse assegnate mentre qualle che hanno valore null appartengono alla lista delle disponibilità (o non assegnate)

    per la lista delle corse assegnate usi "where c.id_b is not null" 

    per quelle da assegnare usi "where c.id_b is null" 

    Filtrando con:

    WHERE c.id_b IS NULL

    rimangono solo le righe senza corrispondenza.

    Differenze tra NOT EXISTS e LEFT JOIN ... IS NULL

    NOT EXISTS usa:
    WHERE NOT EXISTS (...)

    Vantaggi

    Semanticamente più chiaro.
    Gestisce bene i valori NULL.
    Spesso è la soluzione preferita per verificare l'assenza di record correlati.

    LEFT JOIN ... IS NULL usa:
    LEFT JOIN ...
    WHERE c.id_b IS NULL

    Vantaggi

    Facile da leggere quando si stanno già facendo join.
    In molti DBMS il piano di esecuzione è equivalente a NOT EXISTS.
    Attenzione a NOT IN

    Molti scrivono:

    SELECT *
    FROM B
    WHERE id_b NOT IN (
       SELECT id_b
       FROM C
    );

    Se nella sottoquery compare anche un solo NULL, il risultato può essere inatteso e non restituire alcuna riga. Per questo NOT EXISTS è generalmente più sicuro.

    In pratica, per cercare i record di B non presenti nella relazione C, la soluzione più robusta è normalmente:

    SELECT b.*
    FROM B b
    WHERE NOT EXISTS (
       SELECT 1
       FROM C c
       WHERE c.id_b = b.id_b
    );

    la select 1 fa riferimento sempre alla relazione.

    la relazione può avere anche delle clausole Where giorno=oggi; where festivo=true; ecc...

    alla fine di tutti i filtri avrai sempre e comunque una tabellaA, una relazione C ed una tabellaB

    quindi 

    SELECT b.*
    FROM B b //filtrata come meglio credi
    WHERE NOT EXISTS (
       SELECT 1
       FROM C c // filtrata come meglio credi
       WHERE c.id_b = b.id_b
    );

    se usi il caso 2 Con LEFT JOIN ... IS NULL

    appena inserisci la relazione (metti i dati nella tabelaC) e riesegui le due query

    query relazione presente (servizi assegnati)

    SELECT b.*
    FROM B b
    LEFT JOIN C c
          ON c.id_b = b.id_b
    WHERE c.id_b IS NOT NULL;

    query relazione non presente (servizi da assegnare)

    SELECT b.*
    FROM B b
    LEFT JOIN C c
          ON c.id_b = b.id_b
    WHERE c.id_b IS NULL;

    l'unica cosa da fare per svuotare la lista servizi assegnati è cancellare il record della relazione (tabellaC)

    per assegnarlo basta inserire il record della relazione in TabellaC

    09/06/2026 - pierpippo ha scritto:

    Non deve vuotarsi mica nulla 

    ho una tabella dalla quale vengono presi dati con varie condizioni una delle quali che essi non siano gia presenti un un'altra tabella:

    colpa mia, mi sono espresso male, è la query che si arricchisce o si svuota non i dati memorizzati nelle tabelle.

    09/06/2026 - pierpippo ha scritto:

    AND NOT EXISTS (SELECT * FROM gbgar_serv_master LEFT JOIN gbgar_serv_detail ON gbgar_serv_master.id_master=gbgar_serv_detail.id_master LEFT JOIN gbgar_corse ON gbgar_serv_detail.id_corsa=gbgar_corse.id_corsa WHERE data='2026-06-06');

    non and not exist segui l'esempio. stai relazionando tabellaA con i dati che non esistono nella tabellaC devi fare riferimanto sempre alla relazione

    esiste l'ID tabellaA where data=oggi? no, allora mostramelo. il senso è questo stai escludendo i dati già relazionati. usa il caso 2 che sebra molto piu' familiare ad una comune select con join.

    non uso i tuoi dati perchè non mi è chiara la gestione dei dati che stai facendo, ma lo do per giusto.

  • Mamma, leggendo le tue istruzioni mi rendo conto che non ho idea di quanto sia profonda la tana del Bianconiglio. 

    Per ora grazie. Appena posso mi studio bene quanto mi hai suggerito e provo ad implementarlo. 

    Ma per quanto ho visto devo innanzitutto capire cosa rappresentano quelle lettere in minuscolo. 

    Cioè devo intendere che le maiuscole A B e C son le mie tabelle. Ok? 

    le minuscole sono liste estratte di dati, come tabelle "provvisorie"? 

    A presto e di nuovo grazie

  • 10/06/2026 - pierpippo ha scritto:

    le minuscole sono liste estratte di dati, come tabelle "provvisorie"? 

    Le query restituiscono dati provvisorio estratti dalle tabelle.

    Se hai tabellaA con id, descrizione ecc...

    E tabellaB con id, descrizione ecc...

    Sql ha la necessità di capire a quale id di quale tabella stai parlando.

    Puoi:

    Select * from tabellaA

    Join tabellaB on tabellaA.id=tabellaB.id

    Where tabellaA.descrizione='bla bla'

    Oppure metti un alias

    Select * from tabellaA a

    Join tabellaB b on a.id=b.id

    Where a.descrizione='bla bla'

    Se hai nomi astronomici è più facile usare gli alias. 

  • Ok, il discorso degli alias ora mi è piu chiaro.

    Mi resta cmq il problema per cui a me pare che il tuo esempio non sia applicabile nel mio caso.
    Cito il tuo esempio:

    10/06/2026 - sihsandrea ha scritto:

    Tabella A
    id_a    | nome
    ---------------
    1    | Rossi
    2    | Verdi
    Tabella B
    id_b    | descrizione
    ---------------------
    10    | X
    20    | Y
    30    | Z
    40    | W
    Tabella C relazione molti-a-molti (m a m) tra A e B
    id_a    | id_b
    --------------
    1    | 10
    1    | 20
    2    | 30

    In questo caso il valore 40 della tabella B non è presente nella relazione C.

    caso 1. Con NOT EXISTS

    SELECT b.*
    FROM B b
    WHERE NOT EXISTS (
       SELECT 1
       FROM C c
       WHERE c.id_b = b.id_b
    );
    Risultato
    id_b    | descrizione
    ---------------------
    40    | W

    Da quanto leggo e forse capisco la tabella C portata in esempio contenente la relazione tra A e B è una tabella che dovrei considerarla fisica.

    La mia tabella che mette in realzione 

    • gbgar_serv_master (alias M) 
    • con gbgar_serv_detail (alias D)
    •  e a sua volta è messa in relazione con gbgar_corse (alias C)

    Non è una tabella fisica ma una doppia join.

    Quindi:

    la SELECT che devo fare, per passarne i valori alla pagina .php, dove ho il form con la tendina le cui option sono determinate appunto dal risultato della SELECT che intendo impostare,
    deve prelevare i dati da C , ma il risultato dovrebbe scaturire da un confronto per determinare i record i cui id_corsa non appaiono ancora in D.

    E fin qua ci siamo direi.

    Il mio problema è che devo inserire pure la variabile $data_servizio.
    Il valore di questa variabile viene registrata in M nel campo 'data'.

    cioè se io scrivo:

    SELECT linea,partenza,orario_corsa  
    ->FROM gbgar_corse C 
    ->WHERE NOT EXISTS(
    ->SELECT * FROM gbgar_serv_detail D 
    ->WHERE D.id_corsa=C.id_corsa);

    Ottengo una lista dove non appaiono le corse assegnate, cioe gli id_corsa già presenti i D....

    SELECT linea,partenza,orario_corsa  FROM gbgar_corse C WHERE NOT EXISTS(SELECT * FROM gbgar_serv_detail D WHERE D.id_corsa=C.id_corsa);
    +-----------+---------------------+--------------+
    | linea     | partenza            | orario_corsa |
    +-----------+---------------------+--------------+
    | linea 157 | Forlì-P.BUS         | 14:37:00     |
    | linea 159 | Ravenna c.ne Molino | 06:30:00     |
    +-----------+---------------------+--------------+
    2 rows in set (0,001 sec)

    Se non che, senza la clausola della data le corse con il loro id_corsa, nella tabella D ci saranno sicuramente, e quindi la lista che otterrei sarà sempre vuota.

    Ma il confronto deve avvenire tra i record che per la data x, nella tabella D non hanno nessun record con  id_corsa esistenti tra i record della tabella C.

     Quindi:

    la tabella di confronto (quella con le relazioni) deve mettere a disposizione i valori del campo 'data' presente in M, e in sostanza i valori del campo  id_corsa presente in D.

    SELECT C.* FROM gbgar_corse C 
    ->WHERE NOT EXISTS(
    ->SELECT * FROM gbgar_serv_master M 
    ->LEFT JOIN gbgar_serv_detail D 
    ->ON M.id_master=D.id_master 
    ->LEFT JOIN gbgar_corse C 
    ->ON D.id_corsa=C.id_corsa
    ->WHERE data='2026-06-06');
    Empty set (0,001 sec)
    

    mettendo * nella sub query prendo tutti i campi di M e D e C  

    Non capisco perchè mi da Empty quando gli id_corsa 1 e 2 esistono già in D e quindi lista avrebbe dovuto mostrare gli id_corsa 3 e 4 .

    Errori non me da ma non ottengo il risultato atteso.

    Dal confronto tra le due SELECT citate sopra pare che il problema sia nel fatto che ho esigenza di inserire la clausola riguardante la data.
    Spero di non dover spostare il campo 'data' tra la tabella M e la tabella D, dovrei andare poi a modificare tutti gli script php che fanno insert into e update su tali tabelle.

    Buona serata a tutti.

  • Ciao a tutti 

    ci ho messo un pomeriggio ma credo di aver finalmente impostato la select corretta:

    SELECT id_corsa,linea,partenza,DATE_FORMAT(orario_corsa,'%H:%i')AS orario_corsa
    ->FROM gbgar_corse C 
    WHERE NOT EXISTS(
    ->SELECT 1 FROM gbgar_serv_master M
    ->JOIN gbgar_serv_detail D 
    ->ON M.id_master=D.id_master 
    ->where D.id_corsa=C.id_corsa AND M.data='2026-06-06');
    

    Ora le due tabelle in relazione con quella che devo estrarre sono entrambe inserite nella sub query .

    La condizione che relazione i due campi id_corsa della tabella estratta e quella di confronti è impostata e per finire il filtro con la data che esclude dalla lista gli id_corsa già presenti ma per la data inserita.

    Cambiando la data la tendina che mi prende il risultato della select e le schiaffa nelle option che contiene torna in automatico a mettere a disposizione la lista comlpleta.

    Gazie a tutti. In particolare per la pazienza di Sihsandrea.

    Filippo P.

  • Evviva!

    Ora.questa struttura ti può servire per avere l'elenco mezzi, elenco tragitti assegni un tragitto ad un automezzo e spariscono dalla lista da assegnare per finire in quella assegnati. E lo stesso per tutti i casi simili.

Devi accedere o registrarti per scrivere nel forum
22 risposte