SQL Server 2005, Calculating upcoming birthdays from date of birth - sql

This one has bugged me for a while now. Recently when revisiting some code I wrote for a customer a few years ago I was wondering if there is a more elegant solution to the problem.
The customer stores all of their clients information including date of birth (date time field)
They run an extract every Monday that retrieves any customer whose birthday will fall within the following week.
I.e. if the extract was run on Monday Jan 1st, Customers whose birthday fell between (and including) Monday Jan 8th -> Sunday Jan 14th would be retrieved.
My solution was to use the Datepart(dy) function and calculate all upcoming birthdays based off the customers date of birth converted to day of year, adding some logic to include for the extract being run at the end of a year.
The problem was that using Day of year throws results off by 1 day if the customer was born on a leap year and / or the extract is run on a leap-year after the 29th of Feb, so once again I had to add more logic so the procedure returned the expected results.
This seemed quite over-kill for what should be a simple task
For simplicity let’s say the table 'customer' contains 4 fields, first name, last name, dob, and address.
Any suggestions on how to simplify this would really be appreciated
Wes

Would something like this work for you?
select * from Customers c
where dateadd(year, 1900-year(dob), dob)
between dateadd(year, 1900-year(getdate()), getdate())
and dateadd(year, 1900-year(getdate()), getdate())+7

Why not use DATEPART(wk) on this year's birthday?
SET DATEFIRST 1 -- Set first day of week to monday
SELECT * FROM customer
WHERE DATEPART(wk, DATEADD(yy, DATEPART(yy, GETDATE()) - DATEPART(yy, customer.dob), customer.dob)) = DATEPART(wk, GETDATE()) + 1
It selects all customers who's birthday's weeknumber is one greater than the current weeknumber.

I think DATEADD should do the proper thing.

YEAR(GETDATE() - dbo.Patients.Dob) - 1900
I can safely assume you will never have customers born before 1900

Please Try This one.
SELECT TOP 10 BirthDate, FirstName
FROM Customers
WHERE DATEPART(mm,BirthDate) >= DATEPART(mm,GETDATE())
AND DATEPART(day,BirthDate) >= DATEPART(day,getdate())
OR DATEPART(mm,BirthDate) > DATEPART(mm,getdate())
ORDER BY DatePart(mm,BirthDate),DatePart(day,BirthDate)
this query will get upcoming birthdays including today itself

Related

Calculating orders placed on the end of the month

I'm currently studying SQL Server using the book Ben-Gan, Itzik. T-SQL Fundamentals. Below is a query used to select order placed at end of the month. (I know that function EOMONTH() can also be used)
SELECT orderid, orderdate, custid, empid
FROM Sales.Orders
WHERE orderdate = DATEADD( month, DATEDIFF( month, '18991231', orderdate), '18991231');
The author's explanation is:
This expression first calculates the difference in terms of whole
months between an anchor last day of some month (December 31, 1899, in
this case) and the specified date. Call this difference diff. By
adding diff months to the anchor date, you get the last day of the
target month.
However, I'm still a bit confused as to how it actually works. Would someone kindly explain it?
That seems like a rather arcane way to do this. What the code is doing is calculating the number of months since the last day of some month. Then, it adds this number of months to that date. Because of the rules of dateadd(), the month remains the last date.
However, I prefer a simpler method:
where day(dateadd(day, 1, orderdate)) = 1
I find this much clearer.
select DATEDIFF(MONTH, '20160131', '20160201')
give us 1 month and
SELECT DATEADD(month, 1, '20160131')
give us 2016-02-29 00:00:00.000
that's ok
I tried out the query myself and seem to have got the hang of it. here is what i wrote just in case anyone else is interested
SELECT DATEADD(month, DATEDIFF(MONTH, '20160131', '20160201'), '20160131');
result:
2016-02-29 00:00:00.000
so my interpretation is that adding one or more "month" to a particular date in which the last date of the month is 31 will always return a date in which the date is the last day of the month. if this sentence makes any sense...

Get items that were created exactly one, two, n... years ago

From some table I should select items, that were created exactly at least one year ago - at the same month and day as today. So, items created at 2011-10-10, 2013-10-10 will suit, but not at 2014-10-10.
The following query works just fine, but maybe there is a more elegant way? Thanks in advance.
select * from myTable
where
DATEPART(MONTH, CreatedOn) = DATEPART(MONTH, GETDATE()) -- item was created at the same month
and DATEPART(DAY, CreatedOn) = DATEPART(DAY, GETDATE()) -- and at the same day
and DATEDIFF(YEAR, CreatedOn, GETDATE()) >= 1 -- and at least one year ago
I would (if possible) change the table to have an extra, computed column, defined as:
DATEADD(year,DATEDIFF(year,CreatedOn,0),CONVERT(date,CreatedOn))
Say, called AnniversaryDate. This has the effect of "normalizing" the dates into the year 1900. This also treats 29th February the same as 28th February (both get normalized to 28th).
You now create an index on this computed column, making searches within it quick and easy. You use the same formula to transform GETDATE() into a "normalized" date for searching this data. All you have to do now is exclude results that occurred this year, which should be as simple as one extra filter. So your final query wound be:
AnniversaryDate = DATEADD(year,DATEDIFF(year,GETDATE(),0),
CONVERT(date,GETDATE())) and
CreatedOn < CONVERT(date,GETDATE())
(I'd probably create a single index on both AnniversaryDate and CreatedOn, to maximally benefit from this query).

SQL - get last month data

I have been using this query to extract information from last month
SELECT *
FROM Member
WHERE DATEPART(m, date_created) = DATEPART(m, DATEADD(m, -1, getdate()))
with the end of the year approaching, will this automatically pull Dec 2012 when i run it in Jan 2013 ?
Yes. your getdate() function will give the current date when the query is run. And you are adding -1 to the month and comparing month of date_created column and the last month. But I think you should also do comparison of year. You should add two conditions month and year both.
Yes, it will pull December data. But it will pull December data from any year, not just 2012
Yes, it will. DATEADD is a SQL internal function that adds to the full date, not just the selected part (day, month, year).

How to return weeks of data (Sun to Sun)

I'm trying to return some data based on a series of weeks. This is going to be for an SSRS report.
For example, I need to return all rows which are dated from 3rd July to 9th July inclusive (Sun-Sat), name it Week 1 of the month. Then 10th Jul to 16th July, which is Week 2, and so forth. The hardest part is having it continue on between months. For example, 26th June to 2nd July.
I'm sure I can use DATEPART to do this somehow, but really have no idea how. Can someone offer assistance?
Thank you in advance.
DATEPART(week, DateField) will give you the week of the year, which will cover your criteria. Week of the month is not a great criteria to use since it's so amorphous.
You can get the week of the year for the first of the month by returning DATEPART(week, '7/1/2011') or whatever, which will give you a starting point.
EDIT:
For your additional criteria from the comments:
SELECT <fields>
FROM MyTable
WHERE YEAR(DateField) = 2011
AND DATEPART(week, Datefield) BETWEEN
DATEPART(week, '6/1/2011') AND DATEPART(week, (DATEADD(DAY, -1, (DATEADD(Month, 1, '6/1/2011')))))
You may want to check the closing parentheses count, but basically this says:
Year 2011
Week of 6/1/2011 through Week of 6/30/2011
The extra date calculations are to get to 7/1/2011 (month +1) then back a day to 6/30/2011. The alternative is to hardcode date breaks for each month. This way you can also paramterize the dates and concatenate like
CAST(#Month + '/1/' + #Year as smalldatetime)
I suggest the use of something similar to a date dimension. There are tons of examples of how you do this. Here is one:
http://prologika.com/CS/forums/p/517/2094.aspx
Well I will give a more exact answer because no one has left anything yet
Declare #myDate datetime = getdate()
select DATEPART(week, #myDate) - DATEPART(week,CONVERT(DATETIME, DATENAME(mm, #myDate)+"/01/"+ DATENAME(yyyy, #myDate) ))-1
This should give you a good approximation you might want to check my parens.

SQL Query to Count Number of Days, Excluding Holidays/Weekends

I have a "workDate" field and a "receivedDate" field in table "tblExceptions." I need to count the number of days beteen the two. workDate always comes first - so, in effect, it's kind of like workDate is "begin date" and receivedDate is "end date". Some exclusions make it tricky to me though:
First, I need to exclude holidays. i do have a table called "tblHolidays", with one field - holidayDate. I have holidays stored up through next year, so maybe I can incorporate that into the query?
Also, most flummoxing is that work dates can never occur on weekends, but received dates can. So, i somehow need to exclude weekends when i'm counting, but allow for a weekend if the received date happens to fall on a saturday or sunday. so, if the work date is June 3rd, 2011, and received date is June 11th, 2011, the count of valid days would be 7, not 9.
Any help on this is much appreciated. Thanks!
Something like this should give you the number of days with the holidays subtracted:
select
days = datediff(day, workDate, receivedDate)
- (select count(*) from tblHolidays where holidayDate >= workDate and holidayDate < receivedDate)
from
tblExceptions
Note that the date functions differ between database systems. This is based on MS SQL Server, so it may need adapting if you are using some other database.
If you have a table full of dates to include (non-weekends, non-holidays, etc.) and you knew when the 'begin' date and the 'end' date is, then you can do something roughly like this:
select count(*) from tblIncludedDates where beginDateValue <= dateField and endDateValue >= dateField
to get the number of valid days between those dates.