DateDiff function adding an extra year for the customer dob - sql

How can I alter the SQL query to not return a 3 for customer Mike since he is not 3 years old yet. As you can see the customer turns 3 in December of 2021. But my query is giving him the age of 3. Is there anyways to alter or make a query that gives him the correct age?
SELECT
id,
name,
dob,
DATEDIFF(YYYY, dob, GETDATE())
FROM
customer
WHERE
DATEDIFF(YYYY, dob, GETDATE()) >= 2
AND DATEDIFF(YYYY, dob, GETDATE()) <= 4
Results:
id name dob datediff
-------------------------------
1 Mike 2018-12-05 3

There are many varied solutions to this issue on SQL Server. This answer is based on a Stack Overflow answer from a question where the accepted answer is not the best answer imo. When it comes to calculating "age from birth" there are advantages to using date format 112. You could try calculating the customer age something like this
declare
#as_of datetime=getdate(),
#bday datetime='2018-12-05';
select
(0 + Convert(Char(8),#as_of,112) - Convert(Char(8),#bday,112))/10000 age;
age
2

If you want an integer number of years (no fractional part)...
SELECT
*,
CASE
WHEN DATEADD(year, diff.years, customer.dob) > GETDATE()
THEN diff.years - 1
ELSE diff.years
END
AS age
FROM
customer
CROSS APPLY
(
SELECT DATEDIFF(year, customer.dob, GETDATE()) AS years
)
AS diff
WHERE
customer.dob > DATEADD(year, -5, CAST(GETDATE() AS DATE)) -- Born more recently than 5 years ago, so at most 4 years 11 month and 30 days old
AND customer.dob <= DATEADD(year, -2, CAST(GETDATE() AS DATE)) -- Born two years ago or earlier, so at least 2 years and 0 days old
The cross apply is just so that I can write the DATEDIFF() once and refer to it as many times as I like (Don't Repeat Yourself).
But then I also refactored the where clause. By moving the calculation to be on GETDATE() rather than on the dob column, I both make the maths simpler (than the case statement), but also make it so that any index on dob can be used (SARGable).
CAST(GETDATE() AS DATE) just removes the time part of today's date, on the assumption your dob (and calculations) don't account for the exact time they were born ;)

Related

How to find the minimum age from birthday column, but the select only those that are 3 years older than minimum

I have a table, PATIENT, that has birth dates. I need to run a query that returns only those patients that are 3 years older than the youngest.
I am trying to find a good intro tutorial on nested sql statements because my online class is terrible.
This is as far as I've got:
SELECT Phone, Birthday
FROM PATIENT
WHERE Birthday >(
SELECT *
FROM PATIENT
GROUP BY Birthday
ORDER BY Birthday DESC
SELECT MIN(Birthday) AS MinAges
FROM PATIENT);
Don't google for "nested" that is a different thing. Google "subqueries".
SELECT Phone, Birthday
FROM PATIENT
WHERE Birthday - 2 > (
SELECT MIN(Birthday)
FROM PATIENT
);
Here we select those two fields from patient where the birthday is greater then a nested select where we take the least recent birthday and add 3 years to it. Hopefully this points you in the right direction.
SELECT Phone, Birthday
FROM PATIENT
WHERE Birthday >(
SELECT DateAdd(yy, 3, Cast(MIN(Birthday) As datetime))
FROM PATIENT);
If by 3 years older you mean that you want the years of the birthday have a difference of 3 then you can use the DateAdd() function:
select phone, birthday from patient
where year(DateAdd('yyyy', -3, birthday)) = (
select year(min(birthday))
from patient
)
or
select phone, birthday from patient
where year(birthday) - 3 = (
select year(min(birthday))
from patient
)
You can use a subquery to get the oldest birthdate and then date comparisons. This looks like:
select p.*
from patient as p,
(select max(birthday) as max_birthday
from patient
) as m
where p.birthday <= dateadd("yyyy", -3, m.max_birthday) and
p.birthday > dateadd("yyyy", -4, m.max_birthday);
Note that this is precisely 3 years, based on the day of the your

Calculate the age in Microsoft SQL Server Management Studio

I'm doing this but the result give me all the names who they are 30 years and less than 30 years.
This is the database:
Donor ( Donor-ID, First-name, Last-name, Date-of-birth, Sex, Date-of-donate)
Donor-phone (Donor-ID, Phone-number)
Clinic (Clinic-ID, Clinic-name, Clinic-Location)
Blood (Blood-ID, Blood Type)
The donate day (Blood Status, Donate Date)
Employee (Employee-ID, First-name, Last-name, Sex)
I used this code - where is it wrong?
SELECT
GETDATE () AS FirstName,
FirstName, LastName,
DATEADD(DD, 30, GETDATE()) AS [DateOfDonate- DateOfBirth],
DATEADD(DD, 1-1-2000, GETDATE()) AS [DonateOfDate]
FROM
Donor
I need to solve this question: find the names of donors who their age is above 30 years and have donated since 1/1/2000
SELECT
*
FROM
donor
WHERE
[date-of-birth] <= DATEADD(YEAR, -30, GETDATE()) -- Anyone born more than 30 years ago
AND [date-of-donate] >= '2000-01-01' -- Anyone that donated since 1st Jan 2000
Sorry for the previous typo . I calculated with actual fields and then replaced with yours resulting into the mess.
Please use below
cast(datediff(Year,Date-Of-Birth,getdate()) as nvarchar(100))
+ ' Years , '
+ cast(datediff(Month,Date-Of-Birth,getdate())%12 as nvarchar(100))
+ ' months and '
+ cast(datediff(day,Date-Of-Birth,getdate()) as nvarchar(100))
+' days '
select FirstName+" "+ LastName from Donor
where datediff(year,Date-of-birth,getdate()) >30 and Date-of-donate>
convert(date,'Date-of-donate',103)=convert(date,'Date-of-
donate',103)='1/1/2000'
This will work for you

Having case multiple conditions in SQL Server 2014 [duplicate]

This question already has answers here:
Case with multiple conditions in SQL Server 2014
(1 answer)
How to check a SQL CASE with multiple conditions?
(4 answers)
Closed 5 years ago.
I have a table 'FinancialTrans' where only 3 of those fields are needed.
AcctID TransTypeCode DateOfTrans Field 4 Field 5 Field 6....
123 TOLL 2016-06-06
123 TOLL 2016-06-02
123 TOLL 2016-04-28
123 PYMT 2016-03-11
123 TOLL 2015-12-22
123 TOLL 2015-12-22
What I need:
I only need account numbers where there are No Tolls AND No Pymt in the last 2 years.
My attempt at the code:
I know I need a Having clause but not quite sure how to write it.
Perhaps, a NOT Exist?
SELECT [AcctID]
,[TransTypeCode]
,[TransDate]
FROM [FinancialTrans]
WHERE (
(TransTypeCode = 'TOLL' AND Max(TransDate) <= DATEADD(year, -2, GETDATE()))
OR (TransTypeCode = 'PYMT' AND Max(TransDate) <= DATEADD(year, -2, GETDATE()))
)
GROUP BY AcctID, TransTypeCode, TransDate
The challenge I'm facing is that I want account numbers where there is NEITHER a toll NOR a payment in the past two years.
I'm getting account numbers that have no tolls in the past two years but has a payment in the past two years.
Question: How do I ensure I get account numbers that doesn't have BOTH in the past two years?
This question is different from an earlier question asked because the requirements have now changed.
Not exists would work also.
Select AcctID,
TransTypeCode,
TransDate
From FinancialTrans ft1
Where Not Exists (Select 1
From FinancialTrans ft2
Where ft1.AcctID = ft2.AcctID
and ft2.TransTypeCode IN ('TOLL','PYMT')
and ft2.DateOfTrans > DATEADD(year, -2, getdate()))
You can use group by and having:
SELECT [AcctID]
FROM [FinancialTrans]
GROUP BY [AcctID]
HAVING MAX(CASE WHEN TransTypeCode = 'TOLL' THEN TransDate END) <= DATEADD(year, -2, GETDATE()) AND
MAX(CASE WHEN TransTypeCode = 'PYMT' THEN TransDate END) <= DATEADD(year, -2, GETDATE()) ;
That above actually requires that there be both types of transactions. It might be better to do:
SELECT [AcctID]
FROM [FinancialTrans]
GROUP BY [AcctID]
HAVING SUM(CASE WHEN TransTypeCode IN ('TOLL', 'PYMT') AND TransDate > DATEADD(year, -2, GETDATE())
THEN 1 ELSE 0
END) = 0;

MSSQL Date Analysis

I need to write a query where it looks at a plethora of dates and determines if that date was 3 or more years ago, 2 or more years ago, 1 or more year ago, 6 or more months ago, or less than 6 months ago.
Is there a way to do this without writing in physical dates, so that the analysis can be run again later without needing to change the dates?
I have not started to write the query yet, but I have been trying to map it out first.
You should use case. I would recommend something like:
select (case when datecol < dateadd(year, -3, getdate()) as '3 years ago'
when datecol < dateadd(year, -2, getdate()) as '2 years ago'
. . .
end)
I specifically do not recommend using datediff(). It is counterintuitive because it counts the number of "boundaries" between two dates. So, 2016-12-31 and 2017-01-01 are one year apart.
You can use the DATEDIFF function to calculate the number of months, days, years, etc. between two dates, e.g.
select datediff(day, '2016-01-01', '2017-01-01')
returns 366, because 2016 was a leap year
To get the current date, use the GETDATE() function.
I tend to use a generic Tier Table for several reasons.
Logic is moved from code.
Alternate Tiers may be deployed depending on your audience.
Most importantly, things change.
The following will generate a series of dates, and then summarize by the desired tier. I should add, this is a simplified example
Example
-- Create Sample Tier Table
Declare #Tier table (Tier_Group varchar(50),Tier_Seq int,Tier_Title varchar(50),Tier_R1 int,Tier_R2 int)
Insert into #Tier values
('MyAgeTier',1,'+3 Years' ,36,999999)
,('MyAgeTier',2,'2 - 3 Years' ,24,36)
,('MyAgeTier',3,'1 - 2 Years' ,12,24)
,('MyAgeTier',4,'6 Mths - 1 Year',6 ,12)
,('MyAgeTier',5,'<6 Mths' ,0 ,6)
,('MyAgeTier',6,'Total ' ,0 ,999999)
Select Tier_Title
,Dates = count(*)
,MinDate = min(D)
,MaxDate = max(D)
From #Tier A
Join (
-- Your Actual Source
Select Top (DateDiff(DAY,'2010-01-01','2017-07-31')+1)
D=cast(DateAdd(DAY,-1+Row_Number() Over (Order By (Select Null)),'2010-01-01') as date)
From master..spt_values n1,master..spt_values n2
) B
On Tier_Group = 'MyAgeTier' and DateDiff(MONTH,D,GetDate()) between Tier_R1 and Tier_R2-1
Group By Tier_Title,Tier_R1
Order by Tier_R1 Desc
Returns (this example)

SQL date subtraction

In SQL, I'd like to list all funds whose anniversary is due this year in 2 months time. What is the syntax?
SELECT *
FROM dbo.Funds
WHERE AnniversaryDate <= DATEADD(MONTH, 2, GETDATE())
That should work in SQL Server 2000 and up.
Marc
SELECT DATE_ADD(CURDATE(), INTERVAL 2 MONTH);
This will do it in MySQL. I haven't added the anniversary comparison because I don't know the structure of your tables.
I am not quite sure how to interpret your question, but it seems you are after something like this.
SELECT *
FROM funds
WHERE CURRENT_DATE <= anniversary
AND CURRENT_DATE > DATE_SUB(anniversary, INTERVAL 2 MONTH)
It is possibly not exact as I don't know which flavour of SQL you are using.
I don't know if "fund anniversary" is some kind of a special term in English, so I'm assuming you want to select something like the birthdays which can be stored with a random year, like 1972-01-03.
If it's not so, please correct me.
In SQL Server, you need to generate a list of years and join with it:
WITH q AS
(
SELECT 0 AS num
UNION ALL
SELECT num + 1
FROM q
WHERE num <= 100
)
SELECT *
FROM mytable
WHERE fund_date BETWEEN DATE_ADD(year, -num, GETDATE()) AND DATE_ADD(year, -num, DATE_ADD(month, 2, GETDATE()))
UNION ALL
SELECT *
FROM mytable
WHERE fund_date <= DATEADD(year, -100, GETDATE()
AND DATE_ADD(year, YEAR(GETDATE()) - YEAR(fund_date), fund_date) BETWEEN GETDATE() AND DATE_ADD(month, 2, GETDATE()))
This query is built in two parts:
The first part selects birthdays for each year from the list using the index range scans.
The second part selects birthdays that are more than 100 years old (using a single range scan) and filters them all.
Since birthdates older than 100 years are very unlike, the second part in fact selects almost nothing and does not affects performance too much.