Newsgroup: it.comp.appl.access
Titolo: Ricerca "sovrapposizioni"

Ho una tabella con operatore, data, ora inizio e ora fine.
Devo controllare che non sussistano sovrapposizioni, cioe'
che un operatore non compaia due volte lo stesso giorno alla
stessa ora.
Ora sto facendo tutto col codice, ma a pelle sento che forse
c'e' una soluzione sql, che mi piacerebbe molto trovare.
Qualche suggerimento?
Per chi ha bisogno dell'esempietto:
IDOpe	DInizio	HInizio	HFine
gigi	29/3	08.00	10.00
gigi	29/3	14.00	15.00	OK
gigi	29/3	09.30	12.00	ERRORE
Bai
--
Lorenzo Coronati
--
<<Nessuno sa che questa affermazione e' vera>>
(John D. Barrow)
--

stessa ora.
Perche' non gestisci tutto con una bella chiave e relativa intercettazione
dell'errore?:-)
(cioe', si solo ce prova a scrive je fai: a Lello, ma 'ndo vai???:-))
Saluti.
Luciano

...

Lucianè... invece di fa tanto il ganzo, leggi tutto il messaggio per
benino...
Non è solo un problema di scrittura di due record con gli stessi valori nei
campi (caso che andrebbe bene per la tua "soluzione") ma anche di intervalli
contenuti in altri, e questo con le chiavi non lo fai!!!!!
gigi 29/3 08.00 10.00
gigi 29/3 14.00 15.00 OK
gigi 29/3 09.30 12.00 ERRORE
Allora, preparati una query che ordina per nome operatore, data, ora inizio,
ora fine.
Poi te la apri da codice con un recordset. Quindi cominci a scorrerlo:
giocando sulle variabili old_nome, new_nome, old_data, new_data e così via,
quando trovi due record che hanno stesso nome, stessa data ma l'orario
inizio o l'orario fine è compreso nel precedente orario inizio orario fine,
allora hai trovato un record errato e a quel punto potresti accodare in una
tabella di BLOB tutta la famiglia di quel record errato in modo da poi
gestirtelo con calma alla fine dell'elaborazione.
Antonio

(cioe', si solo ce prova a scrive je fai: a Lello, ma 'ndo vai???:-))
Unciarivo.
Come devo da fa?
Me sa che nun se po' fa na chiave come dici te...
Lore', compare de Lello


Poi te la apri da codice con un recordset. 
E' esattamente come ho fatto.
Il che significa che non sei poi cosi' scrauso <:oD
Solo che speravo si potesse fare con sql...
Vabbe', non ho poi tutto questo tempo da perdere ;-)
P.S. gia' iniziato i preparativi per Imola?
(grrr mi rodo il fegato!)


(grrr mi rodo il fegato!)
Sě, abbastanza. Sono stato ieri dal delegato regionale, gran cap fig,
endrocc, direttore di gara nazionale e componente comitato esecutivo CSAI e
mi ha detto che i pass arriveranno da lui lunedi.
Non c'č bisogno di andare a fare le prove libere della formula 3000 giovedi
pomeriggio (meglio) e appuntamento venerdi 7:30 in autodromo.
Spero solo di trovare per dormire...
Nel frattempo sabato e domenica questi antipasto a Vallelunga con Formula
3000, Campus, Arcoblaeno e 3 (una palla, io preferisco le auto a ruote
coperte...)
Antonio

benino...
Hai ragione, allora mi tocca suggerirgli una soluzione molto piu' assai
meglio de la tua, che e' lunga, tediosa e lenta: cioe' scrausetta!:-)
La prima query (Q_ultima_uscita) trova l'ultima uscita
SELECT Last(T_timbrature.uscita) AS UltimoDiuscita
FROM T_timbrature
GROUP BY T_timbrature.nome
HAVING (((T_timbrature.nome)="gigi"))
La seconda accoda solo se la nuova entrata e' posteriore all'ultima uscita
INSERT INTO T_timbrature ( nome, entrata, uscita )
SELECT "gigi" AS chi, #2:30:00 PM# AS nuovaentrata, #7:00:00 PM# AS
nuovauscita
FROM Q_ultima_uscita
WHERE (#2:30:00 PM#)>[ultimodiuscita]
Non funziona se Gigi non ha mai timbrato, ma si puo' provare a gestirlo on
un IIF nella seconda query o fare un controllo sulla prima query e se non
restituisce niente lanciare la seconda senza la condizione WHERE.
Saluti (pure a Biso, va'!)
Luciano

La seconda accoda solo se la nuova entrata e' posteriore all'ultima uscita
OK ho capito.
Questo va bene per un controllo interattivo durante
l'immissione.
A me pero' serve un controllo batch su tabelle gia'
popolate, per cui rimane la strada vba.
Thank you the stess.


               Attenzione all'anti-spam XXX
Prima Legge di Brien:
	Nella vita di qualsiasi organizzazione, la sua capacita' di funzionare
	malgrado se stessa prima o poi si esaurisce.

popolate, per cui rimane la strada vba.
Beh, un recordsettino lo devi fare allora!:-)
Anche perche' immagino che i record sbagliati li vorrai scrivere da qualche
parte, no?
Fai il rst con una query che ordina per nome, data, ora e cicli su quello.
Lancia le due query che ti dicevo, accodando ad una tabella "giusti",
altrimenti ad una "sbagliati".
Ma non confrontando ogni record di Gigi con tutti gli altri suoi, come
diceva Biso, altrimenti non ti passa piu'! :-)
Saluti.
Luciano

diceva Biso, altrimenti non ti passa piu'! :-)
Maddeche?
Differenza inapprezzabile...
Antonio

Differenza inapprezzabile...
Perche' per te confrontare un record con un solo altro o con altri 10 o 100
mila e' la stessa cosa?:-)
Saluti.
Luciano

...

Guarda che non è questa la situazione.
Le famiglie composte da stessa data e stesso utente, potranno avere al
massimo 4 5 record. Quando cominci a confrontare non è detto che incontri
l'errore al 5 record, se lo incontri prima ti fermi su quella famiglia e
passi alla successiva. Nel caso medio quindi confronti 3 record che con i 2
tuoi stiamo lì....
Antonio

La soluzione via codice e' corretta, ma una migliore (nel senso di piu'
compatta) esiste ed e' implementabile in SQL senza troppe complicazioni.
(perfavore se ho travisato il problema non ammazzatemi...)
supponiamo di avere la tabella (chiave di 4 campi):
Timbrature(IDOpe, Dinizio, HInizio, HFine)
Vogliamo vedere per ogni operaio e per ciascuna data che non esistano
finestre temporali sovrapposte (anche solo parzialmente). Quindi vogliamo
verificare che la timbratura di gigi non sia sovrapposta ad
un'altra sua timbratura (nella stessa data).
Vi spiego come ragiono... poi vi propongo la query: prendo la prima
timbratura di gigi e "disegno" la sua finestra temporale. Diciamo che I
(lettera I MAIUSCOLA) e' l'inizio della finestra e F (ancora MAIUSCOLA) e'
la fine. Confronto la finestra di questa timbratura con tutte quelle
timbrature che hanno la stessa data in cui indico con i (lettera i
MINUSCOLA) l'inizio e con f (MINUSCOLA) la fine della relativa finestra.
Sono possibili solo 5 casi:
 
gigi        I------------F
gigi          i--------f            CASO 1
gigi     i-------f                  CASO 2
gigi                i-------f       CASO 3
gigi   i--f                         CASO 4
gigi                        i--f    CASO 5
Spero che la formattazione non sia andata persa, in ogni modo per TUTTI i
casi (e vi invito a verificarlo) vale il predicato seguente:
SOVRAPPOSTO SE e solo SE (I <= f) AND (F >= i)
(occhio a maiuscole minuscole!)
CASO 1: gigi timbra due ingressi e due uscite descrivendo due
finestre in cui una contiene completamente l'altra. SOVRAPPOSTE=True
CASO 2: le finestre sono solo parzialmente sovrapposte (a sinistra).
SOVRAPPOSTE=True
CASO 3: duale del caso 2 con sovrapposizione a destra. SOVRAPPOSTE=True
CASO 4: gigi timbra due ingressi e due uscite nel modo corretto quindi
descrive due finestre attigue (al massimo contigue) ma che non risultano
sovrapposte. SOVRAPPOSTE=False
CASO 5: il duale del caso 4. SOVRAPPOSTE=False
Manca un CASO 6 in cui le due finestre sono perfettamente coincidenti
cioe' sovrapposte con la stessa ora di inizio e la stessa ora di fine, ma
questo caso e' scongiurato sia perche' ragionevolmente impossibile (a
patto di tenere traccia dell'ora precisa al nano-secondo!) sia perche' con
i 4 attributi contemporaneamente come chiave cio' non puo' accadere mai!
Allora ecco la query (con variabili):
SELECT *
FROM Timbrature AS T1
WHERE EXISTS (SELECT *
              FROM Timbrature AS T2
              WHERE (T2.IDOpe = T1.IDOpe) AND
                    (T2.Dinizio = T1.Dinizio) AND
                    (T1.Hinizio <= T2.Hfine) AND
                    (T1.Hfine   >= T2.Hinizio))
ORDER BY HInizio;
Essa restituisce, per ogni tupla in timbrature, TUTTE le tuple che gli si
sovrappongono (con il criterio prima enunciato) afferenti allo stesso
operaio e con la stessa data.
Se tolgo (T2.IDOpe = T1.IDOpe) mi verranno visualizzate le sovrapposizioni
anche tra operai diversi, mentre se tolgo (T2.Dinizio = T1.Dinizio) la
query perde significato perche' non e' restrittivo supporre
sovrapposizioni in date diverse.
Questa query ha un problema che deriva dalla riflessivita' dell'operatore
di uguaglianza: ciascuna cosa e' uguale a se stessa... (mmmh, mi sa che
questa cosa la scrivo come signature :-)) quindi, poiche' la
query simula due cicli FOR innestati, la clausola EXISTS sara'
comunque verificata almeno una volta perche' risultera' che OGNI richiesta
e' sovrapposta a SE STESSA.
Il problema si risolve togliendo l'uguale dai confronti... :-) quindi il
criterio diventa (Criterio 2):
SOVRAPPOSTO SE e solo SE (I < f) AND (F > i)
e la query si trasforma in: 
SELECT *
FROM Timbrature AS T1
WHERE EXISTS (SELECT *
              FROM Timbrature AS T2
              WHERE (T2.IDOpe = T1.IDOpe) AND
                    (T2.Dinizio = T1.Dinizio) AND
                    (T1.Hinizio < T2.Hfine) AND
                    (T1.Hfine   > T2.Hinizio))
ORDER BY HInizio;
 
In questo modo, pero', ammetto impliciamente che CONTESTUALMENTE ad una
timbratura di uscita possa avvenire una timbratura di ingresso che non
genera sovrapposizione... ovvero:
gigi        I---F
gigi    i---f   i---f
Timbrature("gigi", "1/1/99", "10:00", "11:00")
Timbrature("gigi", "1/1/99", "11:00", "12:00")
Timbrature("gigi", "1/1/99", "12:00", "13:00")
restituisce SOVRAPPOSTE=False in base al Criterio 2 (ma non in base al
criterio 1) e quindi e' una sequenza corretta per la seconda query.
In questo modo la query innestata che simula il ciclo FOR interno NON
risente piu' del problema "sovrapposto a me stesso".
In questa applicazione (timbrature di cartellini) appare corretto il
criterio 2 perche' non e' restrittivo supporre l'esistenza di un operaio
che di diverte a "giocare" con la macchinetta delle timbrature generando
uscite contestuali all'ingresso successivo.
Spero di non aver travisato il pb. in tal caso chiedo scusa per un post
cosi' lungo (e noioso...)
ciao
Giuseppe


 cosi' lungo (e noioso...)
Non ho letto attentamente perchè il problema non mi tocca, ma complimenti
per l'esposizione, forse un po' troppo accademica, ma decisamente piacevole
da leggere.
--
Ciao, AntoGal
*************************************************
Home Page (area programmatori di VolFtp)
http://volftp.mondadori.com/italiani/galletta/
per scaricare un gioco di DAMA ITALIANA
*************************************************

compatta) esiste ed e' implementabile in SQL senza troppe complicazioni.
Il tuo ragionamento non fa una piega ed e' proprio
quello che avevo in mente ma che per mancanza di
basi non riesco a mettere in pratica. :-(
Il problema e' che non funziona.
Su 4224 record in tabella, me ne restituisce 4221.
Purtroppo, per lo stesso motivo di cui sopra, non
so trovare l'inghippo. Non voglio farti perdere tempo,
anche perche' il problema lo sto comunque gestendo
via codice, pero' mi piacerebbe venirne a capo.
Se vuoi ti posso mandare un po' di record, per fare
delle prove.
Grazie della disponibilita' e mi associo ad Antonello
nei complimenti per l'esposizione :-)
P.S. relaying mail al tuo server is not allowed ?!?!?


 
Ciao Lorenzo,
in che senso te ne restituisce 4221 su 4224? Individua solo 3
sovrapposizioni? oppure ne individua 4221?
Mandami pure i record... (mdb, txt o come ti fa piu' comodo) saro' gfelice
di aiutarti.
Ciao
Giuseppe
P.S. il pb. del reply to deriva dal mio server. l'indirizzo corretto e'
cer...@cdc700.cdc.polimi.it
 (in cui il server corretto e' cdc700 ma certe
volte esso viene sostituito con cdc7XX-Y che e' il nome della macchina su
cui mi trovo in quel momento)