Newsgroup: it.comp.appl.access
Titolo: filtrare date senza tenere conto dell'anno


Newsgroups: comp.databases.ms-access
 
in pratica, avendo un elenco di targhe e di date di registrazione (queste 
ultime sparpagliate su piů anni) si chiede di scrivere un codice SQl che 
elenchi solo quelle che cadono all'interno di un range giorno/mese (il 
"compleanno", in pratica)
 
il punto cruciale č che il range puň essere a cavallo della fine dell'anno, 
p.e. dal 15/12 al 15/01
 
io ho scritto un po' di spaghetti-code che produce il risultato, altri ci 
stanno pestando la testa, spesso senza aver compreso a fondo i requisiti
 
qui cosa suggerite?
 
-- 
/-\ /\/\ /\/\ /-\ /\/\ /\/\ /-\ T /-\
-=- -=- -=- -=- -=- -=- -=- -=- - -=-
........... [ al lavoro ] ...........

Si da per assunto che la 2° data sia superiore alla 1° spero...
 
Month(campoData)>=Month(MinDate) AND Month(CampoData)<=Month(MaxDate) AND Day(CampoData)>=Day(MinDate) AND Day(CampoData)<=Day(MaxDate)
 
Mi pare regga...?
 
@Alex

 
 
NO
 
come scritto nell'OP non c'è un riferimento all'ANNO, solo mese e giorno
 
giro la domanda: dalla tua rubrica dei contatti, con nome e DATA DI NASCITA 
(giorno/mese/ANNO), tira fuori quelli che compiono gli anni tra il 15 
dicembre e il 24 gennaio


 
 
la butto li
 
avendo come input
 
gg iniziale
gg finale
mm iniziale
mm finale
 
 
accodo tutte le datetarghe in una tabella temporanea sostitundo l'anno 
con un anno a caso esempio 2000
 
costriusco le due date limite
 
se mm finale<mm iniziale
 
datastart=gginizilae+mminiziale+2000
datastop=ggfinale+mmfinale+2001
 
altrimenti
 
datastart=gginizilae+mminiziale+2000
datastop=ggfinale+mmfinale+2000
 
select targa from tabellatemporanea where datatarga between datastart 
and datastop
 
BFS

 
 
molto simile a quella che mi sono inventato io, grazie


 ........... [ al lavoro ] ...........
 
Boh
@Alex

 
 
qui non ti capisco, ho riproposto il "quiz" in maniera molto semplice con i 
compleanni, cosa significa andare avanti o indietro?
 
-- 


 
senza tabella temporanea..mi pare funzioni
 
 
 
declare @minday as int
declare @minmonth as int
declare @maxday as int
declare @maxmonth as int
 
set @minday = 13
set @minmonth = 12
set @maxday = 15
set @maxmonth = 01
 
 
declare @datastart as smalldatetime
declare @datastop as smalldatetime
 
 
 
if @maxmonth<@minmonth
	begin
	set @datastart=DATEFROMPARTS(2020,@minmonth,@minday)
	set @datastop=DATEFROMPARTS(2021,@maxmonth,@maxday)
	select * from Targhe where 
datefromparts(2020,month(datatarga),day(datatarga)) between @datastart 
and datefromparts(2020,12,31)
	union
	select * from Targhe where 
datefromparts(2021,month(datatarga),day(datatarga)) between 
datefromparts(2021,01,01) And @datastop
	end
else
	begin
	set @datastart=DATEFROMPARTS(2020,@minmonth,@minday)
	set @datastop=DATEFROMPARTS(2020,@maxmonth,@maxday)
	select * from Targhe where 
datefromparts(2020,month(datatarga),day(datatarga)) between @datastart 
and @datastop
	end
 
 

 
 
sě, provata sul database degli ordini, č ok


La fuzione da usare è questa e non contiene l'anno e funziona secondo me.
 
Month(campoData)>=Month(MinDate) AND Month(CampoData)<=Month(MaxDate) AND Day(CampoData)>=Day(MinDate) AND Day(CampoData)<=Day(MaxDate) 
 
Saluti
@Alex


Ho detto una bella cazzata ehehehe
Lascia stare, ma ci ragiono meglio 
 
@Alex

 
 
 
ottimo
BFS


dopo aver letto le risposte di @alex credo che non serva neppure 
l'anno...e quindi si può evitare la funzione datefrompart
credo non si scappi però da dover usare una union per il caso in cui il 
mese finale sia inferiore a quello iniziale
 
quindi è ulteriormente migliorabile in termini di costi computazionali
 
 
 
BFS
 
 
 
 
 
 

 BFS
 
Un dubbio ragionando... il campo oggetto di analisi ha sicuramente l'anno...
Basterebbe comporre il between con un iif se MeseFine<MeseInizio e prendere l'anno del campo data sommando appunto 1Anno solo se ....?
Si compone la data cosi:
 
Between Dateserial(year(campodata),MeseInizio,GiornoInizio) AND
Dateserial(Dateadd('y',-(MESEinizio > Mesefine)),MseFine,Giornofine)
 
Non riesco a provarlo... quindi spero non sia la seconda cavolata... ma è caldissimo.
 
@Alex
 

 Lascia stare, ma ci ragiono meglio
 
non ti preoccupare, è un esercizio di programmazione


 La fuzione da usare è questa e non contiene l'anno e funziona secondo me
 
"secondo me" significa che la hai provata su un db campione di pochi record 
e hai effettivamente visto il risultato che ti aspettavi? chiedo perché 
prima di arrivare a una query funzionante IO ho cannato diverse volte :)


Vabbè dopo aver sparato qualche cavolata da CALORE... questa l'ho testata dopo essermi riappropriato del PC.
 
Assunto:
MeseINI=12  GiornoINI=15
MeseEND=1  GiornoINI=15
 
SELECT 
T1.ID
, T1.Cognome, T1.Nome, T1.Nascita, TSel.IniDate, TSel.EndDate FROM T1
RIGHT JOIN (
SELECT ID, CDate(DateSerial(Year([Nascita]),MeseINI,GiornoINI)) AS IniDate, CDate(DateSerial(Year([Nascita])+1,MeseEND,GiornoEND)) AS EndDate, T1.Nascita FROM T1) AS TSel
ON 
T1.ID
=TSel.ID AND (T1.Nascita Between TSel.Inidate And TSel.EndDate) WHERE 
T1.ID
 Is Not Null
 
Provate se vi torna... io ho realizzato uina mascherina conl'immissione dei Mesi/Giorni e costruito il predicato SQL, quindi eseguita la Query.
 
@Alex

Non avevo il PC quindi non potevo provare nulla... 
 
Di fatto diventa così:
 
SELECT 
T1.ID
, T1.Cognome, T1.Nome, T1.Nascita, TSel.IniDate, TSel.EndDate FROM T1
RIGHT JOIN (
SELECT ID, CDate(DateSerial(Year([Nascita]),12,15)) AS IniDate, CDate(DateSerial(Year([Nascita])+1,1,15)) AS EndDate, T1.Nascita FROM T1) AS TSel
ON 
T1.ID
=TSel.ID AND (T1.Nascita Between TSel.Inidate And TSel.EndDate) WHERE 
T1.ID
 Is Not Null
 
Testata su una Tabella di Anagrafiche di qualche centinaio di utenti... e a me pare restituire i dati corretti.
 
 @Alex


Manca un pezzo che è nel codice ... il campo EndDate della Tabella derivata ha un +1 in realtà vedete il risultato della costruzione... che è questo:
 
Private Sub Comando2_Click()
    Dim sSQL    As String
 
    sSQL = "SELECT 
T1.ID
, T1.Cognome, T1.Nome, T1.Nascita, TSel.IniDate, TSel.EndDate FROM T1 RIGHT JOIN ("
    sSQL = sSQL & "SELECT ID, CDate(DateSerial(Year([Nascita])," & Me.tMIni & "," & Me.tGIni & ")) AS IniDate, CDate(DateSerial(Year([Nascita])+" & Abs((Me.tMend < Me.tMIni)) & "," & Me.tMend & "," & Me.tGEnd & ")) AS EndDate, T1.Nascita FROM T1)  AS TSel "
    sSQL = sSQL & " ON (
T1.ID
=TSel.ID) AND (T1.Nascita Between TSel.Inidate And TSel.EndDate) WHERE 
T1.ID
 Is Not Null;"
 
    Debug.Print sSQL
    CurrentDb.QueryDefs("Query1").SQL = sSQL
End Sub
 
@ALex


no, salvo errori miei di copiatura
prova con
 
mario nato 16/12/1980
simone nato 13/01/1981
rosa nato 15/06/1982
 
 > Assunto:
 > MeseINI=12  GiornoINI=15
 > MeseEND=1  GiornoINI=15
 
dovrebbe restituirmi sia mario che simone
 
ma invece restituisce solo mario
 
non credo ci siano alternativa alla union nel caso di mese finale minore 
del mese iniziale
 
if mese iniziale>mese finale then
 
select da giorno/mese iniziale al 31/12
union
select da 01/01 al giorno/mese finale
 
else
	select banale
end if
 
 
BFS


Assunto:
MeseINI=12  GiornoINI=15
MeseEND=1  GiornoINI=15
 
il debug print del tuo codice mi restituisce
 
SELECT 
T1.ID
, T1.Cognome, T1.Nome, T1.Nascita, TSel.IniDate, 
TSel.EndDate FROM T1 RIGHT JOIN (SELECT ID, 
CDate(DateSerial(Year([Nascita]),12,15)) AS IniDate, 
CDate(DateSerial(Year([Nascita])+1,1,15)) AS EndDate, T1.Nascita FROM 
T1)  AS TSel  ON (
T1.ID
=TSel.ID) AND (T1.Nascita Between TSel.Inidate 
And TSel.EndDate) WHERE 
T1.ID
 Is Not Null;SELECT 
T1.ID
, T1.Cognome, 
T1.Nome, T1.Nascita, TSel.IniDate, TSel.EndDate FROM T1 RIGHT JOIN 
(SELECT ID, CDate(DateSerial(Year([Nascita]),12,15)) AS IniDate, 
CDate(DateSerial(Year([Nascita])+1,1,15)) AS EndDate, T1.Nascita FROM 
T1)  AS TSel  ON (
T1.ID
=TSel.ID) AND (T1.Nascita Between TSel.Inidate 
And TSel.EndDate) WHERE 
T1.ID
 Is Not Null;
 
 
e con questo dati
 
mario nato 16/12/1980
simone nato 13/01/1981
rosa nato 15/06/1982
 
mi da solo mario.....manca simone
secondo me ti frega l'anno..
 
BFS


scusa ho incollato due volte il predicato
il debug mi da
 
SELECT 
T1.ID
, T1.Cognome, T1.Nome, T1.Nascita, TSel.IniDate, 
TSel.EndDate FROM T1 RIGHT JOIN (SELECT ID, 
CDate(DateSerial(Year([Nascita]),12,15)) AS IniDate, 
CDate(DateSerial(Year([Nascita])+1,1,15)) AS EndDate, T1.Nascita FROM 
T1)  AS TSel  ON (
T1.ID
=TSel.ID) AND (T1.Nascita Between TSel.Inidate 
And TSel.EndDate) WHERE 
T1.ID
 Is Not Null;
 
 
ma l'errore persiste
 
 
secondo me l'errore è qui
SELECT T1.id, CDate(DateSerial(Year([Nascita]),12,15)) AS IniDate, 
CDate(DateSerial(Year([Nascita])+1,1,15)) AS EndDate, T1.nascita, T1.nome
FROM T1;
 
perchè se simone è nato il  13/01/1981 la tua query calcola 
iniDate=15/12/1981 e endDate 15/01/1982
 
e simone non ci cade dentro
 
 
BFS
 
 
 
 
 

 BFS
 
Corretto Daniele, giusta osservazione... un passo alla volta deve essere più semplice possibile... 
 
Ci ragiono ancora... ;-) ma potrebbe essere la strada da percorrere con gli adeguati aggiustyamenti... da trovare.
 
@Alex
 

 WHERE ((Month([datan]) In (1,2,3,12)));
 
Se la soglia minima fosse 15 Dicembre... il nato il 14 Dicembre verrebbe preso... ma erroneamente, stessa cosa per la soglia massima...
 
La soluzione di Daniele ed Ammammata sicuramente funziona, quindi da validare, mi pare troppo complessa nell'insieme per un problema appartentemente così banale, sicuramente sono io che sottovaluto...
 
@Alex


chi è Daniele? ahahah
 
BFS
(Fabio)

 (Fabio)
 
Scusa Fabio, non so da dove mi salta fuori ma ero convinto ti chiamassi Daniele...
 
@Alex

SELECT xxx.dataN
FROM xxx
WHERE (((Month([datan])) In (12,1,2,3)) AND ((IIf(Month([datan])=12,Day([datan])>15,-1))<>False) AND ((IIf(Month([datan])=3,Day([datan])<15,-1))<>False));


Temo che le Date LIMITE, siano partametri Flessibili, quindi quello che scrivi con IN(12,1,2,3) in realtà va determinato... ed anche la logica del controllo dei giorni... e questo rende più complessa la cosa, o meglio rende obbligatorio scrivere codice per costruire la SINTASSI SQL... e, se si usa il VBA per fare tutto poi diventa poco scalabile su un RDBMS.
 
Quindi meglio la soluzione FULL_SQL proposta, anche se è pur vero che non è applicabile in JET...
 
@Alex

Dal momento che le cose non risolte mi lasciano male... vi sottopongo questo esperimento se vi va di provarlo:
 
Usa a base di Appoggio una Query, nella quale creo i 2 Campi DataIni e DataEnd, ricavando l'Anno dalla DataNascita e, aggiungo 1 anno alla DataEnd SOLO se il MeseEnd<MeseIni per i ragionamenti fatti sopra...!
DataIni=15/12   Mese 12
DataEnd=18/1  Mese 1
In questo caso la DataEnd avrà l'anno+1
 
Abbiamo visto che anche questa non copriva tutte le condizioni... 
 
Questa soluzione fa questo ma considera 2 Date di Compleanno... quella Registrata, e [+1] per fare rientrare nel Range quello che è scappato con l'assunto precedente.
Private Sub CmdSQL_Click()
    Dim sSQL    As String
    Dim sSQLT   As String
 
    sSQL = "SELECT 
T1.ID
,T1.Nascita,T1.Cognome,"
    sSQL = sSQL & "DateSerial(Year([Nascita])," & Me.tMIni & "," & Me.tGIni & ") As dTIni, "
    sSQL = sSQL & "DateSerial(Year([Nascita])+ " & Abs(Val(Me.tMend) < Val(Me.tMIni)) & "," & Me.tMend & "," & Me.tGEnd & ") As dTEnd, "
    sSQL = sSQL & "[Nascita]+365 As Nascita1 "
    sSQL = sSQL & "FROM T1"
 
    sSQLT = "SELECT * FROM (" & sSQL & ") As TSel WHERE (TSel.Nascita Between TSel.dTIni AND TSel.dTEnd) OR (TSel.Nascita1 Between TSel.dTIni AND TSel.dTEnd)"
 
    Debug.Print sSQLT
    CurrentDb.QueryDefs("Query1").SQL = sSQL
End Sub
 
Uso una SUB_SELECT solo per rendere più chiaro il tutto, ma basta una sola Query... ho testato su 100 Records qualche prova e mi pare dare i risultati corretti... ma vorrei chiedervi un test nel caso mi sia sfuggito qualche dettaglio.
 
Se funzionasse... mi pare sia possa essere facilmente scalabile su qualsiasi RDBMS e credo sia discretamente performante... anche con JET riducendola ad 1 SELECT.
 
@Alex

Ho cancellato un pezzetto:
 
era:
CurrentDb.QueryDefs("Query1").SQL = sSQL  <----- Manca la T
 
Deve essere:
CurrentDb.QueryDefs("Query1").SQL = sSQLT
 
@Alex


funziona
BFS
 

 BFS
 
Bene.
 
Questa è la versione Parametrica singola:
 
PARAMETERS mIni Byte, mEnd Byte, dIni Byte, dEnd Byte;
SELECT *
FROM T1
WHERE (((T1.Nascita) Between DateSerial(Year([Nascita]),mIni,dIni) And DateSerial(Year([T1].[Nascita])+1,mEnd,dEnd))) 
OR ((([T1].[Nascita]+365) Between DateSerial(Year([Nascita]),mIni,dIni) And DateSerial(Year([T1].[Nascita])+1,mEnd,dEnd)));
 
@Alex

Giusto a completamento,
 
Questa la Query:
PARAMETERS pMINI Byte, pMEND Byte, pDINI Byte, pDEND Byte;
SELECT *
FROM T1
WHERE ((T1.Nascita) Between DateSerial(Year([Nascita]),[pMINI],[pDINI]) And DateSerial(Year(T1.Nascita)-([pMEND]<[pMINI]),[pMEND],[pDEND])) OR  
((T1.Nascita+365) Between DateSerial(Year([Nascita]),[pMINI],[pDINI]) And DateSerial(Year(T1.Nascita)-([pMEND]<[pMINI]),[pMEND],[pDEND]));
 
Questo il metodo per usarla:
Private Function OpenQueryRange(MonthIni As Byte, MonthEnd As Byte, Optional DayIni, Optional DayEnd) As DAO.Recordset
    Dim qdf     As DAO.QueryDef
 
    Dim bMIni   As Byte
    Dim bMEnd   As Byte
    Dim bDIni   As Variant
    Dim bDEnd   As Variant
 
    bMIni = MonthIni
    bMEnd = MonthEnd
    bDIni = DayIni
    bDEnd = DayEnd
 
    If IsMissing(DayIni) Then bDIni = 1
    If IsMissing(DayEnd) Then bDEnd = Day(Fix(DateSerial(Year(Date), Month(bMEnd) + 1, 0))) ' Ultimo gg del mese
 
    Set qdf = DBEngine(0)(0).QueryDefs("Q1")
    With qdf
        Debug.Print .SQL
        .Parameters!pMINI = bMIni
        .Parameters!pDINI = bDIni
        .Parameters!pMEND = bMEnd
        .Parameters!pDEND = bDEnd
        Set OpenQueryRange = .OpenRecordset
    End With
    qdf.Close
End Function
 
@Alex

 
 
quel 365 funziona anche con gli anni bisestili?
per il 2100 devo preoccuparmi?


 per il 2100 devo preoccuparmi?
 
Io inizierei a preoccuparmi prima... nel 2100 forse sono secco...!
 
Credi possa bastare un DateAdd("y",1,TuaData)...?
;-)
 
@Alex

 
 
ho impostato il periodo dal 15 dicembre al 15 gennaio
 
la query mi fa uscire anche il 16 gennaio (del 2020)
 
https://i.imgur.com/MANKux3.png
 
 
tutto il resto, dal 17/12/2018 al 15/01/2021 sembra a posto


 io ho scritto un po' di spaghetti-code che produce il risultato
 
ah, giusto nel caso che non siate andati a curiosare nell'altro gruppo, 
questo č il mio parto di dieci minuti:
 
 
 
 
declare @fd as int -- from day
declare @fm as int -- from month
 
declare @td as int -- to day
declare @tm as int -- to month
 
declare @fr as int -- from range
declare @tr as int -- to range
 
set @fd = 15 -- i.e. from 15/12
set @fm = 12
 
set @td = 15 -- i.e. to   15/01
set @tm = 1
 
set @fr = @fm * 50 + @fd -- convert month and day into a single number
set @tr = 
case -- case 'to' is lower then 'from' -> falls in next year
when @tm * 50 + @td < @fr then @tm * 50 + @td + 600 
else @tm * 50 + @td
end
 
select DocNum, DocDate -- using ORDR SAP table ;)
from ORDR
 
where 
case
when @tm * 50 + @td < @fr 
and  DATEPART(month, DocDate) * 50 + DATEPART(day, DocDate) < @fr 
then DATEPART(month, DocDate) * 50 + DATEPART(day, DocDate) + 600
else DATEPART(month, DocDate) * 50 + DATEPART(day, DocDate)
End >= @fr
and 
case 
when @tm * 50 + @td < @fr 
and  DATEPART(month, DocDate) * 50 + DATEPART(day, DocDate) < @fr 
then DATEPART(month, DocDate) * 50 + DATEPART(day, DocDate) + 600 
else DATEPART(month, DocDate) * 50 + DATEPART(day, DocDate)
end <= @tr
 
 
 
 
 
non sono stato lě a limarlo e a semplificarlo, se possibile (c'č un calcolo 
@tm * 50 + @td che si ripete diverse volte)
 
perň si risolve con una sola query, senza UNION e senza appoggi aggiuntivi


 
 
come pure DATEPART(month, DocDate) * 50 + DATEPART(day, DocDate)



Non riscontro questa anomalia, inserito stessi Limiti con data 16/01/2000 ma non viene inclusa.
 
@Alex

 
 
SELECT *
FROM (SELECT 
T1.ID
,
T1.Nascita,
T1.Cognome,
DateSerial(Year([Nascita]),12,15) As dTIni, 
DateSerial(Year([Nascita])+ 1,1,15) As dTEnd, 
[Nascita]+365 As Nascita1 
FROM 
T1
)  AS TSel
WHERE (TSel.Nascita Between TSel.dTIni AND TSel.dTEnd) OR (TSel.Nascita1 
Between TSel.dTIni AND TSel.dTEnd);
 
 
questa è la query generata dal codice VBA (ho messo qualche 'a capo' per 
migliorarne la leggibilità)



Provato e non riscontro quel problema.
La Data che ho inserito, essendo Compleanni, NON sono valorizzate con 'ipotetico Now() che contiene hh:mm:ss, ma semplicemente con gg/mm/aaaa quindi hh:mm:ss=0, credi possa essere questo...?
 
@Alex