2013-03-28 12 views
5

Sto tentando di scrivere una query che trova gli utenti che festeggiano un anniversario mensile con la mia azienda in modo che possiamo attivare una email a loro (cioè, se la data di oggi è 3/27, troverebbe persone che si sono iscritte su 1/27, 2/27, 3/27, 4/27, ecc., indipendentemente dall'anno).Query SQL per trovare gli utenti per l'anniversario mensile

Il mio metodo è trovare utenti la cui parte "giorno" della loro data di registrazione è uguale alla parte "giorno" della data odierna.

SELECT [User Id],[Sign Up Date] 
    FROM [Monthly Account Update] 
WHERE DATEPART(DAY,[Sign Up Date]) = DAY(GETDATE()) 

Tuttavia, questo naturalmente non funziona per questi scenari:

  1. Alla fine di febbraio, non vorrei avere la possibilità di e-mail le persone con il 29, 30 e 31 mensili i giorni dell'anniversario, in quanto non accadono. Idealmente, quando è il 28 febbraio, vorrei solo ammassare persone con 29, 30 e 31 date.

  2. Per ogni altro mese in cui la data scade il 30, non potrei mandare email a persone con 31 ° giorno. Idealmente, vorrei solo ammassare persone con 30 e 31 date.

È possibile eseguire una di queste operazioni con una query SQL?

+0

Che dire sulla prima (o 15) del mese, invece? –

risposta

1

È necessaria una clausola più complessa where. Ecco un metodo di forza bruta:

SELECT [User Id],[Sign Up Date] 
FROM [Monthly Account Update] 
WHERE day([Sign Up Date]) = DAY(GETDATE()) or 
     (month(getdate()) in (4, 6, 9, 11) and day(getdate()) = 30 and DAY([Sign Up Date]) = 31) or 
     (month(getdate()) in (2) and day(getdate()) = 28 and DAY([Sign Up Date]) in (29, 30, 31)) 

Se è feb, cerca il 28 °.

0

provare qualcosa di simile:

Creare una tabella M permanente con una sola colonna [mesi] popolato con i numeri interi da 1 a 1000, e utilizzare una query come questa:

SELECT [User Id],[Sign Up Date] 
FROM [Monthly Account Update] AS U 
JOIN M 
ON CAST(getdate() AS DATE) = DATEADD(month,M.months,U.[Sign Up Date] 

Mentre è non molto efficiente, per un piccolo numero di dipendenti, non è un problema eseguirlo una volta al giorno. Puoi anche aggiungere M. mesi alla lista SELECT e ottenere il numero preciso di anniversari di mesi se lo desideri.

0

Sottrai un mese e includi ogni giorno successivo da un intervallo fino a 4 giorni in cui l'aggiunta del mese non supera la data corrente.

DECLARE @Today DATE = '2/28/2013'; 
SELECT [Current] = @Today, [MonthAgo] = DATEADD(DAY,t.v,DATEADD(MONTH,-1,@Today)) 
FROM (VALUES(0),(1),(2),(3)) as t(v) 
WHERE DATEADD(MONTH,1,DATEADD(DAY,t.v,DATEADD(MONTH,-1,@Today))) <= @Today; 

Risultato per 2013/02/28:

Current MonthAgo 
---------- ---------- 
2013-02-28 2013-01-28 
2013-02-28 2013-01-29 
2013-02-28 2013-01-30 
2013-02-28 2013-01-31 

Risultato per 2013/03/27:

Current MonthAgo 
---------- ---------- 
2013-03-27 2013-02-27 

Risultato per 2013/04/30:

Current MonthAgo 
---------- ---------- 
2013-04-30 2013-03-30 
2013-04-30 2013-03-31 

... ecc.

Edit:

La mia risposta sopra potrebbe essere applicato avvolgendolo in un CTE e poi unirsi alla tua richiesta originale in modo semplice.Si noti che le chiamate di funzione inline sono sempre limitate a quattro righe per tutta la domanda, in modo da impatto sulle prestazioni delle funzioni di data dovrebbe essere trascurabile:

DECLARE @Today DATE = GETDATE(); 

; WITH CTE AS (
    SELECT [Current] = @Today, [MonthAgo] = DATEADD(DAY,t.v,DATEADD(MONTH,-1,@Today)) 
    FROM (VALUES(0),(1),(2),(3)) as t(v) 
    WHERE DATEADD(MONTH,1,DATEADD(DAY,t.v,DATEADD(MONTH,-1,@Today))) <= @Today 
) 
SELECT [User Id],[Sign Up Date] 
FROM [Monthly Account Update] 
JOIN CTE ON CTE.[MonthAgo] = [Sign Up Date]; 
GO 
1

Alcune risposte complicate qui! Siamo in grado di semplificare notevolmente:

create function dbo.fnIsMonthlyAnniversary(
    @startDate date, 
    @comparisonDate date 
) 
returns bit 
as 
begin 
    declare @retVal bit 
    set @retVal = 0 

    declare @deltaMonth int 

    set @deltaMonth = datediff(mm, @startDate, @comparisonDate) 

    if dateadd(mm, @deltaMonth, @startDate) = @comparisonDate 
    begin 
     set @retVal = 1 
    end 

    return @retVal 
end 

-- usage: 
select dbo.fnIsMonthlyAnniversary('2012-12-31', CONVERT(date, getdate())) 

per il vostro uso:

SELECT [User Id],[Sign Up Date] 
    FROM [Monthly Account Update] 
WHERE 1 = dbo.fnIsMonthlyAnniversary([Sign Up Date], CONVERT(date, getdate())) 
+0

Sei sicuro che questo faccia la cosa giusta al mese e negli anni bisestili? Non sto dicendo che non lo sia, ti sto solo chiedendo se sei sicuro. Qualcosa come [@ GordonLinoff's answer] (http://stackoverflow.com/a/15673272), anche se forse "più complicato", ha il pregio della trasparenza. – AakashM

+0

Questo ha il pregio della logica di leva già implementata e testata per anni e anni. DATEADD gestisce tutto correttamente e l'ho testato personalmente, ma dato che la tua reputazione potrebbe essere in linea se usi questo codice, è sempre saggio testarlo tu stesso. È anche molto facile da testare - creare l'UDF, quindi eseguire l'udf usando le date che vuoi. Scoprirai che '2012-12-31', '2013-02-28' restituisce 1, ad esempio. – Moho

+0

Bello, mi ricorderò di questo. – AakashM