Conteggio parziale di giorni in diversi periodi

di il
16 risposte

Conteggio parziale di giorni in diversi periodi

Buona sera a tutti.
Sono al primo post e premetto che il mio livello di programmazione è davvero prossimo allo Zero.
Il problema che vorrei sottoporre oggi, in sintesi, è il seguente:
In una tabella che contiene i periodi di assenza del personale, sono presenti anche quelle dei responsabili di diversi uffici (tutto il personale è identificato con una matricola personale); durante l'assenza, ciascun responsabile viene sostituito (nell'incarico) da un dipendente (non è detto che sia sempre lo stesso) che ne fa le funzioni ad interim.
Io vorrei sapere il dipendente "X", per quanti giorni ha sostituito un responsabile, in uno specifico lasso di tempo che di volta in volta stabilisco in base all'esigenza.
Poniamo che il dipendente X sostituisca un responsabile dal 14/02/2019 al 19/02/2019 e dal 03/04/2019 al 06/04/2019.
Io ho necessità di creare una query che mi faccia sapere per quanti giorni il dipendente X ha "retto" l'ufficio, nel periodo che va dal 17/02/2019 al 30/04/2019?
La risposta è 7... ma come faccio a fare questo conto?
La tabella, per questo aspetto ha i seguenti campi:
[Matricola] matricola personale assente
[dal] assenza
[al] assenza
[MatrSost] matricola del sostituto
Spero di essere stato chiaro. Grazie

16 Risposte

  • Re: Conteggio parziale di giorni in diversi periodi

    Perché gestisci quella tabella con quei campi? Non ti basta una semplice tabella Presenze in cui tracci chi c'è c'è?
  • Re: Conteggio parziale di giorni in diversi periodi

    Purtroppo no.
    Quella tabella è strutturata in quel modo perché è più ampia di quanto descritto in forma stringata.
    Mi è utile anche per altre operazioni, sempre legate alle assenze (e non alle presenze) del personale, tipo lettere e comunicazioni interne di vario genere, che prendono i dati delle assenze proprio da li.
    [edit]
    ..., di fatto ora è già come hai suggerito.
    Il mio problema è il conteggio dei giorni di presenza (o di assenza, in questo contesto è uguale) che tengano conto anche dei parziali dei periodi, ora espressi nei campi [Dal] - [Al].
  • Re: Conteggio parziale di giorni in diversi periodi

    Robpagna ha scritto:


    Quella tabella è strutturata in quel modo perché è più ampia di quanto descritto in forma stringata.
    Mi è utile anche per altre operazioni, sempre legate alle assenze (e non alle presenze) del personale, tipo lettere e comunicazioni interne di vario genere, che prendono i dati delle assenze proprio da li.
    Detto così senza elencare tutti i campi, non mi convinci o non ti seguo.
    Mi pare di capire che tu vuoi gestire sia Presenze che Assenze con Motivazione. Io vedrei la tabella...mi viene di chiamarla Attività con i seguenti campi:
    IDAttività (PK)
    DataAttività
    PA (qui specifichi se si tratta di Presenza o Assenza)
    Motivazione
    IDDipendente (FK)

    A seguire fai tutti i conteggi che vuoi su Presenze e Assenze.
  • Re: Conteggio parziale di giorni in diversi periodi

    Magari mi sono spiegato mane nella risposta che ho dato, e ne chiedo scusa
    Il mio problema è solo quello spiegato nel post iniziale.
    Mi occorre solo sapere nel periodo del quale necessito, per quanti giorni complessivamente il dipendente X ha sostituito un "responsabile".
    Torno a precisare che la mia difficoltà sta nel tener conto anche dei periodi "parziali", esattamente come l'esempio che ho fatto sopra.
    Questo tipo di conteggio non sono in grado di farlo
  • Re: Conteggio parziale di giorni in diversi periodi

    Se ti ho fornito il "mio" suggerimento è perché ritengo che sia giusto "normalizzare" le tabelle e ottenere quello che hai chiesto in maniera "direi" snella/facile/immediata. Personalmente, per fare i conteggi che hai richiesto con quella tabella con quei campi, dovrei inventarmi qualcosa che...neanche io riesco a capire da dove dovrei iniziare.
    Ci capita spesso di leggere quesiti che partono sbagliati in partenza (struttura tabelle non adeguata). Di conseguenza non ha nemmeno senso continuare sulla strada errata.
    Per essere più sicuri di quello che dico dovresti esporre:
    - nomi propri di tutte le tabelle coinvolte nel tuo discorso
    - nomi propri di tutti i loro campi, con indicazione della chiave primaria
    - le relazioni
    In alternativa potresti allegare una immagine della Finestra Relazioni.
  • Re: Conteggio parziale di giorni in diversi periodi

    La tabella "Assenze", è così strutturata:
    [Id] chiave primaria automatica
    [CIP] Matricola personale
    [Dal] Data inizio assenza
    [Al] Data fine assenza
    [CipSost] Matricola del sostituto
    [gg_Compl] Giorni di assenza (frutto del calcolo automatico [Al]-[Dal]+1)
    ... ci sono altri campi dove vengono memorizzati per ciascuna assenza, e dove necessario, il tipo di assenza, il numero di protocollo della lettera che, in base al tipo, viene prodotta attraverso diversi report predisposti, mansione del dipendente,.. comunque tutti finalizzati a stampe legate alla singola assenza.
    Esiste anche una tabella "Personale" dove sono inseriti tutti i dati del personale, ivi compresa la matricola [CIP] che è impostata come chiave primaria.
    Tra queste due al momento non ci sono relazioni dirette; nella maschera di inserimento delle assenze, seleziono il personale attraverso dei menù a tendina, che prendono i dati dalla tabella "personale".
    in fine, se può essere utile, aggiungo che le tabelle vere e proprie sono in un db SQL, alle quali Access è collegato.
  • Re: Conteggio parziale di giorni in diversi periodi

    Il mio punto di vista è il seguente:
    1. Le tabelle Dipendenti (o Personale come la chiami tu) deve essere relazionata con la tabella Assenze (come la chiami tu così adesso).
    2. Resto fermo dell'idea sulla mia tabella Attività con i campi che ho elencato io (al posto di Assenze).
    3. Calcolo conteggi Presenze e Assenze separatamente e relativamente a ciascun Dipendente con apposite query di CONTEGGIO.

    Stop. Altro non saprei.
  • Re: Conteggio parziale di giorni in diversi periodi

    Robpagna,
    dal momento che hai una struttura dati basata su intervalli periodici ([Dal] Data inizio assenza e [Al] Data fine assenza) e non una situazione giornaliera (con cui sommare direttamente lo status delle assenze nel periodo) visto, ulteriormente, che nello stesso record identifichi il personale a livello matricolare (del soggetto e del suo sostituto) dovrai effettuare un calcolo basato sull'intervallo di ricerca e se questo ricada nel periodo di assenza.
    Nel particolare, si dovrà esaminare l'intervallo di ricerca in modo da determinare se è sovrapponibile (in tutto o in parte) o non sovrapponibile.
    Dal punto di vista pratico è sufficiente una query che esamini i vari casi, calcoli i giorni ed effettui la loro somma.
    Si avranno i vari casi (fra gli intervalli) in modo che qualora:
    - sia sovrapponibile in tutto (intervallo di ricerca all'interno del periodo di assenza); si assegna intero periodo dei giorni ([Dal] - [Al] +1);
    - sia sovrapponibile parzialmente con i casi (comprendendo gli estremi) di:
    > data di partenza della ricerca all'interno del periodo di assenze; si assegnano i giorni trascorsi fra la data di partenza della ricerca e quella di fine delle assenze;
    > data di termine della ricerca all'interno del periodo di assenze; si assegnano i giorni trascorsi fra la data di inizio assenze e quella del termine della ricerca;
    - non sia sovrapponibile; il valore dei giorni è pari a zero.
    In Access, per ottenere ciò si impiega l'operatore Between e la funzione DateDiff.
  • Re: Conteggio parziale di giorni in diversi periodi

    Grazie Willy per la risposta. Hai centrato il mio problema.
    Puoi indicarmi come impostare tale query?
  • Re: Conteggio parziale di giorni in diversi periodi

    Come ti ho indicato devi realizzare la query impostando i casi possibili degli intervalli.
    Procedi magari per gradi e determini ciascun intervallo (con Between ... And o con gli operatori di confronto) impostando Vero o Falso alla condizione.
    Ad esempio per determinare se la data di Partenza (con cui effettui la ricerca) ricade nell'intervallo delle assenze (Dal, Al) puoi impiegare un codice similare:
    
    IIf([Partenza]>=[Dal] And [Partenza]<=[Al]; True; False) 
    
    ed allo stesso modo determinando gli altri casi.
    Ottenuti i quattro possibili casi, con una ulteriore IIF (che può anche essere annidata) puoi impiegare la funzione DateDiff, similarmente a:
    
    GG_parte_iniziale: DateDiff("g";[Partenza];[Al])+1
    
    (e cosi via per gli altri casi) in modo da calcolare i possibili giorni dell'intervallo della assenza per ciascuna condizione.
  • Re: Conteggio parziale di giorni in diversi periodi

    Problema SEMBRA semplice, ma la soluzione NON E' semplice.
    il gioco con gli intervalli e' un possibile approccio, ma forse serve una approccio piu' organico.

    Serve saper programmare, ma un approccio alternativo potrebbe essere questo.
    escludiamo, per ora, il caso di intervalli a cavallo di un anno.

    Ti serve

    1) un vettore di numeri interi di 366 elementi (bisogna considerare anche gli anni bisestili)
    2) un modo per convertire una data in un numero di giorni passati a partire dal 1 gennaio
    3) per ogni intervallo di date, converti le singole date nel corrispondente intero (indice), e metti ad 1 l'elemento del vettore corrispondente a quel indice
    4) puoi fare un ulteriore controllo e mettere a 0 le date che corrispondono a sabato/domenica, feste, permessi, malattia, ...

    Ora ti ritrovi con un vettore in cui sono ad 1 gli elementi corrispondenti alle date che ti interessano valido per un intero anno

    5) conti il numero di 1 nell'intervallo che ti interessa.

    Se il tuo intervallo e' a cavallo di 2 anni, lo spezzi in due intervalli, uno che termina il 31 dicembre e l'altro che inizia il 1 gennaio. Se e' a cavallo di piu' anni, ragioni allo stesso modo

    Se non vuoi avere il problema degli anni, puoi creare un vettore di 3660 elementi (10 anni) o 36600 (100 anni). Per il computer un vettore di 36.000 elementi e' meno di un bricciola. I problemi nascono quando ragioni in termini di MILIONI di elementi.

    Per lavorare con gli intervalli, bisogna considerare parecchi casi.
    Intanto la convenzione: l'intervallo [a,b] indica che le date a e b SONO COMPRESE.
    Ora, dati due intervalli A=[a,b] e C=[c,d], si hanno i seguenti casi

    - A precede B se b<c
    - A succede B se a>d
    - A contiene B se a<=c AND b>=d
    - A e' contenuto in B se a>=c AND b<=d
    - A e B sono uguali se a=c AND b=d
    - A e B si intersecano se a<c AND c<= b AND b<=d
    - A e B si intersecano se c<=a AND a<=d AND d<b

    A partire da queste regole, bisogna implementare le OPERAZIONI tra intervalli.
    Come minimo intersezione, unione, e lunghezza di un intervallo.

    Ci sono diversi problemi con gli intervalli :
    - se questi sono a cavallo di un fine settimana, si rischia di contare il fine settimana come giorni di lavoro.
    - il risultato e' una serie di intervalli, NON un unico intervallo

    Come si vede, la 'complessita' per risolvere il problema e' abbastanza elevata

    Il giochino con il vettore e' equivalente a lavorare con intervalli multipli (fino a 366..36600) e ogni intervallo e' lungo solo un giorno.

    Contettualmente molto piu' semplice.
  • Re: Conteggio parziale di giorni in diversi periodi

    Cerca su internet in Sql query annidate e query di raggruppamento
  • Re: Conteggio parziale di giorni in diversi periodi

    E' evidente che siamo molti passi avanti rispetto alle mie effettive capacità.

    Partendo dal primo codice suggerito da Willy55 (che ringrazio), sto facendo alcune prove, sostituendo il "True" e "False" (che non saprei come utilizzare) con valori da attribuire alla condizione data, del tipo:
    Partenza: IIf([datainiziorichiesta]>=[Dal] And [datainiziorichiesta]<=[Al]; [datainiziorichiesta]; [Dal)]
    Rientro: IIf([dataifinerichiesta]<=[Al] And [dataifinerichiesta]>=[Dal]; [dataifinerichiesta]; [Al]) 
    Ma sono ancora molto lontano... ma mi sto avvicinando (almeno in parte)
  • Re: Conteggio parziale di giorni in diversi periodi

    Il concetto di impiegare, in una query, le possibili condizioni con cui esaminare i vari intervalli (assegnando Vero o Falso) serve semplicemente per procedere gradualmente (quasi a livello didattico) in modo da poter osservare le modalità con cui il processo avviene (poteva anche essere impiegato un algoritmo compattato o del codice VBA per le funzioni relative).
    Viste le difficoltà cerco di fornirti delle indicazioni, nel merito, più esplicative.
    Immaginiamo (in una ipotesi di tabella semplificata delle assenze) di avere i seguenti dati di partenza:
    
    ID	Dal		Al	
    1	01/01/2015	31/12/2015	
    2	01/01/2016	02/02/2016	
    3	03/03/2016	04/04/2016	
    4	01/06/2016	10/07/2016	
    5	01/06/2016	11/11/2016	
    6	01/10/2016	12/12/2016	
    7	10/10/2016	12/12/2016	
    8	12/12/2016	31/12/2016
    
    e si voglia ricercare i giorni di assenza che ricadono nel periodo con intervallo:
    
    Partenza	Termine
    01/07/2016	10/10/2016
    
    Per prima cosa si realizza la query con i campi ([Dal] , [Al]) e si imposta (per semplicità) i valori di ricerca (Partenza, Termine) in modo fisso (dopo si provvederà ad implementare con parametri o attraverso una maschera di selezione delle date).
    Il codice SQL sarà qualcosa del genere:
    
    SELECT TabellaPeriodi.Dal, TabellaPeriodi.Al, #7/1/2016# AS Partenza, #10/10/2016# AS Termine
    FROM TabellaPeriodi;
    
    Implementerai questa query inserendo il controllo sui vari intervalli (ove siano sovrapponibili le date di ricerca all'interno del periodo di assenza).
    
    Partenza_Interno: IIf([Partenza]>=[Dal] And [Partenza]<=[Al];Vero;Falso)
    Termine_Interno: IIf([Termine]>=[Dal] And [Termine]<=[Al];Vero;Falso)
    PartenzaTermine_Esterni: IIf([Partenza]<[Dal] And [Termine]>[Al];Vero;Falso)
    
    Il codice SQL risultante sarà qualcosa del genere:
    
    SELECT TabellaPeriodi.Dal, TabellaPeriodi.Al, #7/1/2016# AS Partenza, #10/10/2016# AS Termine, 
           IIf([Partenza]>=[Dal] And [Partenza]<=[Al],True,False) AS Partenza_Interno, 
           IIf([Termine]>=[Dal] And [Termine]<=[Al],True,False) AS Termine_Interno, 
           IIf([Partenza]<[Dal] And [Termine]>[Al],True,False) AS PartenzaTermine_Esterni
    FROM TabellaPeriodi;
    
    Attraverso i campi logici calcolati (Partenza_Interno, Termine_Interno, PartenzaTermine_Esterni) si potrà determinare (in base alle loro combinazioni) i vari tipi di intervallo e procedere al relativo calcolo.

    Giusto per rendere più chiaro l'esempio, si possono calcolare (in ogni caso) i giorni dei possibili intervalli nel caso in cui:
    - sia sovrapponibile in tutto (intervallo di ricerca all'interno del periodo di assenza) si assegna quindi intero periodo (ad esempio nel campo calcolato: GG_tutto_periodo);
    - sia sovrapponibile parzialmente, con la data di partenza della ricerca all'interno del periodo di assenze, si assegnano i giorni trascorsi fra la data di partenza della ricerca [Partenza] e quella di fine delle assenze [Al] (ad esempio nel campo calcolato: GG_parte_iniziale);
    - sia sovrapponibile parzialmente, con la data di termine della ricerca all'interno del periodo di assenze, si assegnano i giorni trascorsi fra la data di inizio assenze [Dal] e quella del termine della ricerca [Termine] (ad esempio nel campo calcolato: GG_parte_finale);
    - non sia sovrapponibile in tal caso o la ricerca comprende il periodo (Dal-Al) oppure il valore dei giorni è pari a zero e quindi è inutile il relativo calcolo.

    Per implementare i calcoli dei relativi giorni negli intervalli si può porre:
    
    GG_tutto_periodo: DateDiff("g";[Dal];[Al])+1
    GG_parte_iniziale: DateDiff("g";[Partenza];[Al])+1
    GG_parte_finale: DateDiff("g";[Dal];[Termine])+1
    
    Il codice SQL implementato sarà qualcosa del genere:
    
    SELECT TabellaPeriodi.Dal, TabellaPeriodi.Al, #7/1/2016# AS Partenza, #10/10/2016# AS Termine, 
          IIf([Partenza]>=[Dal] And [Partenza]<=[Al],True,False) AS Partenza_Interno, 
          IIf([Termine]>=[Dal] And [Termine]<=[Al],True,False) AS Termine_Interno, 
          IIf([Partenza]<[Dal] And [Termine]>[Al],True,False) AS PartenzaTermine_Esterni, 
          DateDiff("d",[Dal],[Al])+1 AS GG_tutto_periodo, 
          DateDiff("d",[Partenza],[Al])+1 AS GG_parte_iniziale, 
          DateDiff("d",[Dal],[Termine])+1 AS GG_parte_finale
    FROM TabellaPeriodi;
    
    Infine per ottenere il calcolo dei giorni di assenza relativi all'intervallo impostato, si procede partendo dai due valori logici individuati [Partenza_Interno] e [Termine_Interno] ed in base a questa coppia di valori si determina quale delle condizioni (fra Vero e Falso) sia applicabile, in modo da assegnare i giorni relativi calcolati (GG_tutto_periodo, GG_parte_iniziale, GG_parte_finale) oppure zero.
    Dal punto di vista pratico si può impiegare una IIF nidificata (in modo da gestire le quattro condizioni) e determinare un campo calcolato [GGTot] che stabilisca i relativi giorni di assenza, come nel seguente esempio:
    
    GGTot: 
    IIf([Partenza_Interno]=Vero And [Termine_Interno]=Vero;[GG_tutto_periodo];
    IIf([Partenza_Interno]=Vero And [Termine_Interno]=Falso;[GG_parte_iniziale];
    IIf([Partenza_Interno]=Falso And [Termine_Interno]=Vero;[GG_parte_finale];
    IIf([PartenzaTermine_Esterni]=Vero;DateDiff("g";[Dal];[Al])+1;0))))
    
    Per cui il codice SQL finale, della query, risulta:
    
    SELECT TabellaPeriodi.Dal, TabellaPeriodi.Al, #7/1/2016# AS Partenza, #10/10/2016# AS Termine, 
           IIf([Partenza]>=[Dal] And [Partenza]<=[Al],True,False) AS Partenza_Interno, 
           IIf([Termine]>=[Dal] And [Termine]<=[Al],True,False) AS Termine_Interno, 
           IIf([Partenza]<[Dal] And [Termine]>[Al],True,False) AS PartenzaTermine_Esterni, 
           DateDiff("d",[Partenza],[Termine])+1 AS GG_tutto_periodo, 
           DateDiff("d",[Partenza],[Al])+1 AS GG_parte_iniziale, 
           DateDiff("d",[Dal],[Termine])+1 AS GG_parte_finale, 
           IIf([Partenza_Interno]=True And [Termine_Interno]=True,[GG_tutto_periodo],
           IIf([Partenza_Interno]=True And [Termine_Interno]=False,[GG_parte_iniziale],
           IIf([Partenza_Interno]=False And [Termine_Interno]=True,[GG_parte_finale],
           IIf([PartenzaTermine_Esterni]=True,DateDiff("d",[Dal],[Al])+1, 0 )))) AS GGTot
    FROM TabellaPeriodi;
    
    
    In modo da ottenere il seguente risultato conclusivo:
    
    Dal		Al		Partenza	Termine		Partenza_Interno	Termine_Interno	GG_tutto_periodo	GG_parte_iniziale	GG_parte_finale	GGTot
    01/01/2015	31/12/2015	01/07/2016	10/10/2016	Falso			Falso		102			-182			649		0
    01/01/2016	02/02/2016	01/07/2016	10/10/2016	Falso			Falso		102			-149			284		0
    03/03/2016	04/04/2016	01/07/2016	10/10/2016	Falso			Falso		102			-87			222		0
    01/06/2016	10/07/2016	01/07/2016	10/10/2016	Vero			Falso		102			10			132		10
    01/06/2016	11/11/2016	01/07/2016	10/10/2016	Vero			Vero		102			134			132		102
    01/10/2016	12/12/2016	01/07/2016	10/10/2016	Falso			Vero		102			165			10		10
    10/10/2016	12/12/2016	01/07/2016	10/10/2016	Falso			Vero		102			165			1		1
    12/12/2016	31/12/2016	01/07/2016	10/10/2016	Falso			Falso		102			184			-62		0
    
Devi accedere o registrarti per scrivere nel forum
16 risposte