Det finns flera fall där man vill ta reda på om någonting inträffar t.ex nästa dag, vecka eller månad. Det kan vara en födelsedag eller en annan högtidsdag.
För att kunna göra denna jämförelse behöver man tre datum; ett datum för när någonting inträffade och två datum som anger inom vilket tidsintervall man vill undersöka om händelsen kommer att inträffa igen.
Det finns en inbyggs funktion i SQL Server för att ta reda på en tidsdifferens mellan två datum och den heter DATEDIFF. Var bara beredd på att den inte uppför sig om man kan förvänta sig. Läs mer här http://www.sqlteam.com/article/datediff-function-demystified.
Kontentan är att om dagen för händelsen ligger senare i månaden än vad dagens datum är får man lägga till en extra månad för att kompensera för hur hjärnan fungerar och hur DATEDIFF fungerar.
Börja med att deklarera tre datum
DECLARE @DoB DATETIME,
@FromDate DATETIME,
@ToDate DATETIME;
För att kunna kontrollera om händelsen kommer att inträffa inom det givna tidsintervallet behöver vi en referenspunkt och två händelsedatum att jämföra med varandra. I vårt fall blir referenspunkten @DoB. Sedan får vi helt enkelt undersöka hur många hela månader som inträffat till den ena händelsedatumet @FromDate och dessutom hur många hela månader som inträffat till det andra händelsedatumet @ToDate.
CASE
WHEN DATEPART(DAY, @DoB) >= DATEPART(DAY, @FromDate) THEN DATEDIFF(MONTH, @DoB, @FromDate) – 1
ELSE DATEDIFF(MONTH, @DoB, @FromDate)
END / 12
Sedan gör du likadant, nästan, med det andra datumet. När jag säger nästan så är det för att om händelsedatumet ligger exakt på det andra datumet så får vi hantera det lite annorlunda.
CREATE FUNCTION dbo.fnHasAnniversary
(
@DoB DATETIME,
@FromDate DATETIME,
@ToDate DATETIME
)
RETURNS INT
WITH RETURNS NULL ON NULL INPUT
AS
BEGIN
DECLARE @From INT,
@To INT
IF @FromDate > @ToDate
SELECT @From = CASE
WHEN DATEPART(DAY, @DoB) >= DATEPART(DAY, @ToDate) THEN DATEDIFF(MONTH, @DoB, @ToDate) – 1
ELSE DATEDIFF(MONTH, @DoB, @ToDate)
END / 12,
@To = CASE
WHEN DATEPART(DAY, @DoB) > DATEPART(DAY, @FromDate) THEN DATEDIFF(MONTH, @DoB, @FromDate) – 1
ELSE DATEDIFF(MONTH, @DoB, @FromDate)
END / 12
ELSE
SELECT @From = CASE
WHEN DATEPART(DAY, @DoB) >= DATEPART(DAY, @FromDate) THEN DATEDIFF(MONTH, @DoB, @FromDate) – 1
ELSE DATEDIFF(MONTH, @DoB, @FromDate)
END / 12,
@To = CASE
WHEN DATEPART(DAY, @DoB) > DATEPART(DAY, @ToDate) THEN DATEDIFF(MONTH, @DoB, @ToDate) – 1
ELSE DATEDIFF(MONTH, @DoB, @ToDate)
END / 12
RETURN NULLIF(@To, @From)
END
Det blir inte svårare än så här. Jag tänkte istället berätta om två kanske nya funktioner som inte är så kända. Den första är “RETURNS NULL ON NULL INPUT”. Det gör att funktion själv känner av att om en av parametrarna är NULL så exekveras ingen kod i funktionen alls, utan funktionen returnernerar NULL direkt.
Den andra funktionen är NULLIF. Den funktionen returnerar NULL om båda parametrarna är lika med varandra. I vårt fall passar det det, för om den första åldern är lika med den andra åldern så har ingen födelsedag inträffat och då returnerar funktionen NULL. Om de två variablerna däremot är olika varandra, t.ex om @From är 39 och @To är 40, så har personen fyllt 40 under det angivna tidsintervallet och då vill vi returnera 40 istället för 39. Då sätter vi @From före @To.
Funktionen är enkel att använda.
SELECT Name,
DateOfBirth,
dbo.fnHasAnniversary(DateOfBirth, ‘20130603’, ‘20130609’) AS Age
FROM dbo.Employees
Lycka till!