Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
| Author |
Topic |
|
kpgraci
Yak Posting Veteran
68 Posts |
Posted - 2011-02-16 : 14:14:22
|
| I am trying to write a birth-date query. The user enters a start_date and end_date and I want to retrieve all clients who have a dob in that range.So I really want to discount the year portion of the dates, unless the end_date extends into the next year.So some cases may be: Feb/1/2011 to Mar/1/2011 or Nov/15/2010 to Jan/20/2011 for example. It would make no sense to have a range that extends over a year, because that would select all clients, so it can be assumed if end_date_month > start_date_month then we are talking about a data range that includes Jan 1 and is less than 1 year in length.I could do this in multiple queries since I have a data class responsible for returning the results, so appending multiple query results is not a problem, but perhaps a performance issue. In this case I could query only the month portions, but this still leaves a problem of mid-month dates, for example, how would I select the range Nov/15 to Nov/30, returning all DOB in that date ranges regardless of year?Any help appreciated!kpg |
|
|
lazerath
Constraint Violating Yak Guru
343 Posts |
Posted - 2011-02-16 : 14:58:44
|
| [code]-- Input parametersDECLARE @start_date datetime = '11/1/11', @end_date datetime = '2/14/12';-- Test data setupDECLARE @BirthDates TABLE(BIRTH_DATE DATETIME);INSERT @BirthDates VALUES('2010-03-17 00:00:00.000'),('2010-06-18 00:00:00.000'),('2010-09-29 00:00:00.000'),('2009-08-11 00:00:00.000'),('2009-12-31 00:00:00.000'),('2009-07-31 00:00:00.000'),('2009-04-19 00:00:00.000'),('2009-04-28 00:00:00.000'),('2009-03-02 00:00:00.000'),('2009-05-20 00:00:00.000'),('2009-10-12 00:00:00.000'),('2008-08-13 00:00:00.000'),('2010-12-28 00:00:00.000'),('2011-01-11 00:00:00.000'),('2009-08-24 00:00:00.000'),('2009-06-11 00:00:00.000'),('2009-09-28 00:00:00.000'),('2010-11-15 00:00:00.000'),('2009-04-16 00:00:00.000'),('2009-02-07 00:00:00.000');-- First CTE normalizes birthdates to the current yearWITH cteBirthDateCurrentYearAS(SELECT DATEADD(yy,DATEDIFF(yy,bd.BIRTH_DATE,@start_date),bd.BIRTH_DATE) BIRTH_DATEFROM @BirthDates bd)-- Second CTE ensures birthdates match across year boundaries, cteBirthDateCrossyearAS(SELECT DATEADD(yy,CASE WHEN bd.BIRTH_DATE < @start_date THEN 1 ELSE 0 END,bd.BIRTH_DATE) BIRTH_DATEFROM cteBirthDateCurrentYear bd)-- Return all results to show what matches and what doesn'tSELECT *, CASE WHEN c.BIRTH_DATE BETWEEN @start_date and @end_date THEN '**MATCHED**' ELSE '' END AS StatusFROM cteBirthDateCrossyear cORDER BY c.BIRTH_DATE;[/code]Not super happy with this, because this is non-sargable and will generate a table scan every time. If the table in question is small and will always stay small, this should work ok. If you have a lot of data or expect to eventually have a lot of data, you should consider another solution. |
 |
|
|
lazerath
Constraint Violating Yak Guru
343 Posts |
Posted - 2011-02-16 : 15:39:41
|
I don't have showplan rights on our SQL 2008 box, but I believe this method will make use of the index if you have a lot of data:-- Input parametersDECLARE @start_date datetime = '12/31/11', @end_date datetime = '1/1/12';-- Test data setupIF OBJECT_ID('tempdb..#BirthDates') IS NOT NULL DROP TABLE #BirthDates;CREATE TABLE #BirthDates (ID INT IDENTITY(1,1) PRIMARY KEY, BIRTH_DATE DATETIME);INSERT #BirthDatesselect DATEADD(d,(spt1.number + spt2.number * POWER(2,11)) % 40515,'1/1/1900') as BIRTH_DATEfrom master..spt_values spt1JOIN master..spt_values spt2ON spt1.type = spt2.typeAND spt2.number < 512where spt1.[type] = 'P' ORDER BY NEWID(); --randomize insertCREATE NONCLUSTERED INDEX ix_#BirthDates ON #BirthDates (BIRTH_DATE);-- create a CTE to simplify the formula for the WHERE clause in the selectWITH cte1AS(SELECT bd.*,DATEADD(yy,DATEDIFF(yy,bd.BIRTH_DATE,0),bd.BIRTH_DATE) AS NORMALIZED_BIRTH_DATE,DATEADD(yy,DATEDIFF(yy,@start_date,bd.BIRTH_DATE),@start_date) AS start_date,DATEADD(yy,DATEDIFF(yy,@end_date,bd.BIRTH_DATE) + DATEDIFF(yy,@start_date,@end_date),@end_date) AS end_dateFROM #BirthDates bd)SELECT c.ID, c.BIRTH_DATE, c.NORMALIZED_BIRTH_DATEFROM cte1 cWHERE c.BIRTH_DATE BETWEEN DATEADD(yy,CASE WHEN c.BIRTH_DATE < c.start_date THEN -1 ELSE 0 END,c.start_date)AND DATEADD(yy,CASE WHEN c.BIRTH_DATE < c.start_date THEN -1 ELSE 0 END,c.end_date)ORDER BY c.NORMALIZED_BIRTH_DATE, c.BIRTH_DATEIF OBJECT_ID('tempdb..#BirthDates') IS NOT NULL DROP TABLE #BirthDates; |
 |
|
|
|
|
|
|
|