How to check the query has values and get the difference of the start date and end date in intervals in postgres SQL? - postgresql

These are the summary of issue:
There is an issue of not going inside the if clause and data not inserted.
Need to compute the target_progress_date as the difference between the end_date and start_date and all the interval months to be computed and stored in the variable so as to insert into the project_target_progress table.
Viz: Start date :1-1-2021, End date : 1-12-2021 Months: 1-1-2021
1-2-2021 1-3-2021 .... 12-12-2021
// To check if any values are there
$query_check = pg_query(DBCON,"select id , (COALESCE(revised_cost,administrative_sanction_project_cost,award_tender_project_cost) as cost, COALESCE(award_tender_contract_date,administrative_sanction_date,actual_start_date) as start_date, COALESCE(revised_completion_date,scheduled_completion_date)::date) as end_date from sp_index_v4 where id = $proj_id ");
// If it has no null values then execute the logic - Autoset of target progress before editing it.
if ($query_check != null) {
console.log ('Hi'+$query_check);
$query_cost = pg_query(DBCON,"select id ,(COALESCE(revised_cost,administrative_sanction_project_cost,award_tender_project_cost)/ datediff('month',COALESCE(award_tender_contract_date,administrative_sanction_date,actual_start_date)::date, COALESCE(revised_completion_date,scheduled_completion_date)::date)) as cost_of_one_month, TRUNC(((COALESCE(revised_cost,administrative_sanction_project_cost,award_tender_project_cost)/ datediff('month',COALESCE(award_tender_contract_date,administrative_sanction_date,actual_start_date)::date, COALESCE(revised_completion_date,scheduled_completion_date)::date))/ (COALESCE(revised_cost,administrative_sanction_project_cost,award_tender_project_cost)))*100,2) as cost_percent from sp_index_v4 id = $proj_id");
// Insert for new project entry update for already having project
$ins_query = "insert into project_target_progress(project_id,cum_financial_progress_in_cr,cum_financial_progress_in_percent,cum_physical_progress_in_percent,target_progress_date)values('$id',cost_of_one_month,cost_percent,cost_percent,start_date)";
$insert_data = pg_query(DBCON, $ins_query);
}
else {
echo "Value cannot be inserted";
console.log("inside the else block");
}

I wrote my own datediff function with different opportunities:
create or replace function datediff
(
units varchar(30),
start_t timestamp,
end_t timestamp
)
returns int as $$
declare
diff_interval interval;
diff int = 0;
years_diff int = 0;
begin
if units in ('yy', 'yyyy', 'year', 'mm', 'm', 'month') then
years_diff = date_part('year', end_t) - date_part('year', start_t);
if units in ('yy', 'yyyy', 'year') then
return years_diff;
else
return years_diff * 12 + (date_part('month', end_t) - date_part('month', start_t));
end if;
end if;
diff_interval = end_t - start_t;
diff = diff + date_part('day', diff_interval);
if units in ('wk', 'ww', 'week') then
diff = diff/7;
return diff;
end if;
if units in ('dd', 'd', 'day') then
return diff;
end if;
diff = diff * 24 + date_part('hour', diff_interval);
if units in ('hh', 'hour') then
return diff;
end if;
diff = diff * 60 + date_part('minute', diff_interval);
if units in ('mi', 'n', 'minute') then
return diff;
end if;
diff = diff * 60 + date_part('second', diff_interval);
return diff;
end;
$$ language plpgsql;
Now, we can write a sample query using our function:
select
mm.start_date,
mm.end_date,
test.datediff('year', mm.start_date, mm.end_date) as diff_year,
test.datediff('month', mm.start_date, mm.end_date) as diff_month,
test.datediff('day', mm.start_date, mm.end_date) as diff_day
from (
select '2021-11-25 02:20:54.200'::timestamp as start_date, now()::timestamp as end_date
) mm
Result:
start_date
end_date
diff_year
diff_month
diff_day
2021-11-25 02:20:54.200
2022-02-24 06:10:52.258
1
3
91

Related

DB2 - use field as a labeled duration for time calculation

Given a table that looks like this:
id task scheduled_date reminder
-- ----------------------- ---------------- --------
1 mail january newsletter 2022-01-01 15 days
I had planned on executing a query to mimic date addition as in
SELECT TASK, SCHEDULED_DATE + 15 DAYS FROM ...
==> 2022-01-16
Unfortunately, using the REMINDER field gives an error:
SELECT TASK, (SCHEDULED_DATE + REMINDER) FROM ...
==>[Code: -182, SQL State: 42816] [SQL0182] A date, time, or timestamp expression not valid.
Is there any way to accomplish using the reminder field as a labeled duration? (I'm using IBMi DB2)
You'll need to convert the string "15 days" into an actual duration.
A date durration is a decimal(8,0) number representing YYYYMMDD
So 15 days would be 00000015
1 year, 00010000
1 year 1 month, one day '00010101`
create table testdur (
datedur decimal(8,0)
);
insert into testdur
values (15), (10000), (10101), (90), (300);
select current_date as curDate
, dateDur
,current_date + dateDur
from testdur;
Results
There is an attempt to implement the interval function available in Db2 for LUW. It supports string expression as a parameter, not just string constant as the built-in one.
The result of this function can participate in whatever allowed date arithmetic.
This works on Db2 for LUW v11.1+ and Db2 for IBM i v7.5+ at least.
create or replace function interval_d (p_interval varchar (100))
returns dec (8)
contains sql
deterministic
no external action
begin atomic
declare v_sign dec (1) default 0;
declare v_pattern varchar (100) default '([+-]? *[0-9]+) *(\w+)';
declare v_y int default 0;
declare v_m int default 0;
declare v_d int default 0;
declare v_occ int default 1;
declare v_num int;
declare v_kind varchar (10);
l1: while 1=1 DO
set v_kind =
lower
(
regexp_substr
(
p_interval
, v_pattern
, 1, v_occ, '', 2
)
);
if v_kind is null then leave l1; end if;
set v_num =
int
(
replace
(
regexp_substr
(
p_interval
, v_pattern
, 1, v_occ, '', 1
)
, ' ', ''
)
);
if sign (v_num) * v_sign < 0 then
signal sqlstate '75001' set message_text = 'Sign of all operands must be the same';
end if;
if v_sign = 0 then set v_sign = sign (v_num); end if;
if v_kind in ('d', 'day', 'days')
then set v_d = v_d + v_num;
elseif v_kind in ('mon', 'mons', 'month', 'months')
then set v_m = v_m + v_num;
elseif v_kind in ('y', 'year', 'years')
then set v_y = v_y + v_num;
else
signal sqlstate '75000' set message_text = 'wrong duration';
end if;
set v_occ = v_occ + 1;
end while l1;
if abs (v_d) > 99 then
set v_m = v_m + v_d / 30, v_d = mod (v_d, 30);
end if;
if abs (v_m) > 99 then
set v_y = v_y + v_m / 12, v_m = mod (v_m, 12);
end if;
return v_y * 10000 + v_m * 100 + v_d;
end
select interval_d (i) as d
from
(
values
('4 years 2 months 3 days')
, ('3 day 4 year 2 month')
, ('-4y -2mon -3d')
) t (i)
D
40203
40203
-40203
fiddle

How to use temporary variables in select query in Postgresql equivalent to Mysql?

I am migrating DB from Mysql to Postgresql, so that Iam struggling to use temporary variables in the select query as it giving error near ':=' Please go through the below queries for reference...
Mysql query:
SELECT
#totalHours \:= FLOOR(
TIMESTAMPDIFF(HOUR, StartDate, EndDate)
) AS totalHours
FROM
tableName #expectedEnd = CASE
WHEN #totalHours = 0 THEN
(
act.EndDate + INTERVAL '1 DAY'
)
ELSE
EndDate
END AS expectedEnd #totalHours = CASE
WHEN #totalHours = 0 THEN
(#totalHours + 24)
ELSE
#totalHours
END AS exptotalHours;
Postgresql query:
SELECT
#totalHours := abs(
extract(
epoch
FROM
(StartDate - EndDate)
) / 3600
) AS totalHours
FROM
tableName #expectedEnd = CASE
WHEN #totalHours = 0 THEN
(EndDate + INTERVAL '1 DAY')
ELSE
EndDate
END AS expectedEnd #totalHours = CASE
WHEN #totalHours = 0 THEN
(#totalHours + 24)
ELSE
#totalHours
END AS exptotalHours;
Note: Declaring a variable first and reusing the same in other select query is not possible in my case
I have tried in different ways to store the value in temporary vaiables, but I couldn't... Anybody please give me a solution for this
I appreciate your kind help

How do you get a dynamic 12 business day view in Postgresql?

Here is the code I currently have that gives me the last 12 days
SELECT *
FROM table
WHERE analysis_date >= current_date - interval '12' day;
analysis_date is the date column in the table. I understand why this isn't working because it's not accounting for business days. How can I rewrite this so that I get an interval of the last 12 business days?
I tried search online and found
extract(dow from (date))
But I couldn't find an example where I need a weekday interval. Any help would be appreciated.
This can be solved with a CTE:
WITH business_days_back AS (
WITH RECURSIVE bd(back_day, go_back) AS (
-- Go back to the previous Monday, allowing for current_date in the weekend
SELECT CASE extract(dow from current_date)
WHEN 0 THEN current_date - 6
WHEN 6 THEN current_date - 5
ELSE current_date - extract(dow from current_date)::int + 1
END,
CASE extract(dow from current_date)
WHEN 0 THEN 7
WHEN 6 THEN 7
ELSE 12 - extract(dow from current_date)::int + 1
END
UNION
-- Go back by the week until go_back = 0
SELECT CASE
WHEN go_back >= 5 THEN back_day - 7
WHEN go_back > 0 THEN back_day - 2 - go_back
END,
CASE
WHEN go_back >= 5 THEN go_back - 5
WHEN go_back > 0 THEN 0
END
FROM bd
)
SELECT back_day FROM bd WHERE go_back = 0
)
SELECT * FROM my_table WHERE analysis_date >= (SELECT * FROM business_days_back);
Some explanation:
The inner CTE starts off by working back to the previous Monday, compensating for a current_date that falls on a weekend day.
The recursive term then adds rows by going back full weeks (back_day - 7 for the calendar date and go_back - 5 for the business days) until go_back = 0.
The outer CTE returns the back_day date where go_back = 0. This is therefore a scalar query and you can use it as a sub-query in a filter expression.
You can change the number of business days to look back by simply changing the numbers 12 and 7 in the initial SELECT in the inner CTE. Keep in mind, though, that the value should be such that it goes back to the previous Monday or the query will fail, due to the same initial SELECT of the inner CTE.
A far more flexible (and probably faster*) solution is to use the following function:
CREATE FUNCTION business_days_diff(from_date date, diff int) RETURNS date AS $$
-- This function assumes Mon-Fri business days
DECLARE
start_dow int;
calc_date date;
curr_diff int;
weekend int;
BEGIN
-- If no diff requested, return the from_date. This may be a non-business day.
IF diff = 0 THEN
RETURN from_date;
END IF;
start_dow := extract(dow from from_date)::int;
calc_date := from_date;
IF diff < 0 THEN -- working backwards
weekend := -2;
IF start_dow = 0 THEN -- Fudge initial Sunday to the previous Saturday
calc_date := calc_date - 1;
start_dow := 6;
END IF;
IF start_dow + diff >= 1 THEN -- Stay in this week
RETURN calc_date + diff;
ELSE -- Work back to Monday
calc_date := calc_date - start_dow + 1;
curr_diff := diff + start_dow - 1;
END IF;
ELSE -- Working forwards
weekend := 2;
IF start_dow = 6 THEN -- Fudge initial Saturday to the following Sunday
calc_date := calc_date + 1;
start_dow := 0;
END IF;
IF start_dow + diff <= 5 THEN -- Stay in this week
RETURN calc_date + diff;
ELSE -- Work forwards to Friday
calc_date := calc_date + 5 - start_dow;
curr_diff := diff - 5 + start_dow;
END IF;
END IF;
-- Move backwards or forwards by full weeks
calc_date := calc_date + (curr_diff / 5) * 7;
-- Process any remaining days, include weekend
IF curr_diff % 5 != 0 THEN
RETURN calc_date + curr_diff % 5 + weekend;
ELSE
RETURN calc_date;
END IF;
END; $$ LANGUAGE plpgsql STRICT IMMUTABLE;
This function can take any date to calculate from and any number of days into the future (positive value of diff) or the past (negative value of diff), including diffs within the current week. And since it returns the business day date as a scalar, use in your query is very straightforward:
SELECT *
FROM table
WHERE analysis_date >= business_days_diff(current_date, -12);
Apart from that, you can also pass in fields from your table and do funky stuff like:
SELECT t1.some_value - t2.some_value AS value_diff
FROM table t1
JOIN table t2 ON t2.analysis_date = business_days_diff(t1.analysis_date, -12);
i.e. a self-join on a certain number of business days separation.
Note that this function assumes a Monday-Friday business day week.
* This function does only simple arithmetic on scalar values. The CTE has to set up all manner of structures to support the iteration and the resulting record sets.

Finding previous day of the week

In PostgreSQL 8.4, given a date, if that date is not a Friday, I would like to find the date of the previous Friday. Can someone tell me if there is an inbuilt function or give the logic behind getting my own function.
Try this, works on other days too, blog about it http://www.ienablemuch.com/2010/12/finding-previous-day-of-week.html
create or replace function previous_date_of_day(the_date date, dow int) returns date
as
$$
select
case when extract(dow from $1) < $2 then
$1 - ( extract(dow from $1) + (7 - $2) )::int
else
$1 - ( extract(dow from $1) - $2)::int
end;
$$ language 'sql';
select to_char(z.ds, 'Mon dd yyyy dy') as source,
to_char( previous_date_of_day(z.ds, 5), 'Mon dd yyyy dy') as dest
from
(
select 'Dec 1 2010'::date + x.n as ds
from generate_series(0,17) as x(n)
) as z
You solve it without using case:
select
the_date
from
(
select
now()::date - num as the_date, -- generate rows of possible dates
extract(dow from (now()::date - num)) -- dow for the where condition
from (select generate_series(0,6) as num) as t
) as days
where date_part = 5;
SELECT
CASE
-- 1. if Friday, return date
WHEN EXTRACT(DOW FROM my_date) = 5
THEN my_date
-- 2. if Saturday, subtract 1
WHEN EXTRACT(DOW FROM my_date) = 6
THEN my_date - INTERVAL '1 day'
-- 3. all other days of the week, subtract `DOW + 2` from my_date
-- should be ELSE for future-proofing ;-) MB
ELSE -- WHEN EXTRACT(DOW FROM my_date) < 5 THEN
my_date - ((EXTRACT(DOW FROM my_date) + 2)::TEXT||'days')::INTERVAL
END AS tgif
FROM
my_table
WHERE
my_date IS NOT NULL
select case when extract(dow from your_date) < 5 then
your_date - (extract(dow from your_date) + integer '2')
else when extract(dow from your_date) > 5 then
your_date - integer '1'
else
your_date
end
Reference http://developer.postgresql.org/pgdocs/postgres/functions-datetime.html

How to calculate age in T-SQL with years, months, and days

What would be the best way to calculate someone's age in years, months, and days in T-SQL (SQL Server 2000)?
The datediff function doesn't handle year boundaries well, plus getting the months and days separate will be a bear. I know I can do it on the client side relatively easily, but I'd like to have it done in my stored procedure.
Here is some T-SQL that gives you the number of years, months, and days since the day specified in #date. It takes into account the fact that DATEDIFF() computes the difference without considering what month or day it is (so the month diff between 8/31 and 9/1 is 1 month) and handles that with a case statement that decrements the result where appropriate.
DECLARE #date datetime, #tmpdate datetime, #years int, #months int, #days int
SELECT #date = '2/29/04'
SELECT #tmpdate = #date
SELECT #years = DATEDIFF(yy, #tmpdate, GETDATE()) - CASE WHEN (MONTH(#date) > MONTH(GETDATE())) OR (MONTH(#date) = MONTH(GETDATE()) AND DAY(#date) > DAY(GETDATE())) THEN 1 ELSE 0 END
SELECT #tmpdate = DATEADD(yy, #years, #tmpdate)
SELECT #months = DATEDIFF(m, #tmpdate, GETDATE()) - CASE WHEN DAY(#date) > DAY(GETDATE()) THEN 1 ELSE 0 END
SELECT #tmpdate = DATEADD(m, #months, #tmpdate)
SELECT #days = DATEDIFF(d, #tmpdate, GETDATE())
SELECT #years, #months, #days
Try this...
SELECT CASE WHEN
(DATEADD(year,DATEDIFF(year, #datestart ,#dateend) , #datestart) > #dateend)
THEN DATEDIFF(year, #datestart ,#dateend) -1
ELSE DATEDIFF(year, #datestart ,#dateend)
END
Basically the "DateDiff( year...", gives you the age the person will turn this year, so i have just add a case statement to say, if they have not had a birthday yet this year, then subtract 1 year, else return the value.
Simple way to get age as text is as below:
Select cast((DATEDIFF(m, date_of_birth, GETDATE())/12) as varchar) + ' Y & ' +
cast((DATEDIFF(m, date_of_birth, GETDATE())%12) as varchar) + ' M' as Age
Results Format will be:
**63 Y & 2 M**
Implemented by arithmetic with ISO formatted date.
declare #now date,#dob date, #now_i int,#dob_i int, #days_in_birth_month int
declare #years int, #months int, #days int
set #now = '2013-02-28'
set #dob = '2012-02-29' -- Date of Birth
set #now_i = convert(varchar(8),#now,112) -- iso formatted: 20130228
set #dob_i = convert(varchar(8),#dob,112) -- iso formatted: 20120229
set #years = ( #now_i - #dob_i)/10000
-- (20130228 - 20120229)/10000 = 0 years
set #months =(1200 + (month(#now)- month(#dob))*100 + day(#now) - day(#dob))/100 %12
-- (1200 + 0228 - 0229)/100 % 12 = 11 months
set #days_in_birth_month = day(dateadd(d,-1,left(convert(varchar(8),dateadd(m,1,#dob),112),6)+'01'))
set #days = (sign(day(#now) - day(#dob))+1)/2 * (day(#now) - day(#dob))
+ (sign(day(#dob) - day(#now))+1)/2 * (#days_in_birth_month - day(#dob) + day(#now))
-- ( (-1+1)/2*(28 - 29) + (1+1)/2*(29 - 29 + 28))
-- Explain: if the days of now is bigger than the days of birth, then diff the two days
-- else add the days of now and the distance from the date of birth to the end of the birth month
select #years,#months,#days -- 0, 11, 28
Test Cases
The approach of days is different from the accepted answer, the differences shown in the comments below:
dob now years months days
2012-02-29 2013-02-28 0 11 28 --Days will be 30 if calculated by the approach in accepted answer.
2012-02-29 2016-02-28 3 11 28 --Days will be 31 if calculated by the approach in accepted answer, since the day of birth will be changed to 28 from 29 after dateadd by years.
2012-02-29 2016-03-31 4 1 2
2012-01-30 2016-02-29 4 0 30
2012-01-30 2016-03-01 4 1 2 --Days will be 1 if calculated by the approach in accepted answer, since the day of birth will be changed to 30 from 29 after dateadd by years.
2011-12-30 2016-02-29 4 1 30
An short version of Days by case statement:
set #days = CASE WHEN day(#now) >= day(#dob) THEN day(#now) - day(#dob)
ELSE #days_in_birth_month - day(#dob) + day(#now) END
If you want the age of years and months only, it could be simpler
set #years = ( #now_i/100 - #dob_i/100)/100
set #months =(12 + month(#now) - month(#dob))%12
select #years,#months -- 1, 0
NOTE: A very useful link of SQL Server Date Formats
Here is a (slightly) simpler version:
CREATE PROCEDURE dbo.CalculateAge
#dayOfBirth datetime
AS
DECLARE #today datetime, #thisYearBirthDay datetime
DECLARE #years int, #months int, #days int
SELECT #today = GETDATE()
SELECT #thisYearBirthDay = DATEADD(year, DATEDIFF(year, #dayOfBirth, #today), #dayOfBirth)
SELECT #years = DATEDIFF(year, #dayOfBirth, #today) - (CASE WHEN #thisYearBirthDay > #today THEN 1 ELSE 0 END)
SELECT #months = MONTH(#today - #thisYearBirthDay) - 1
SELECT #days = DAY(#today - #thisYearBirthDay) - 1
SELECT #years, #months, #days
GO
The same sort of thing as a function.
create function [dbo].[Age](#dayOfBirth datetime, #today datetime)
RETURNS varchar(100)
AS
Begin
DECLARE #thisYearBirthDay datetime
DECLARE #years int, #months int, #days int
set #thisYearBirthDay = DATEADD(year, DATEDIFF(year, #dayOfBirth, #today), #dayOfBirth)
set #years = DATEDIFF(year, #dayOfBirth, #today) - (CASE WHEN #thisYearBirthDay > #today THEN 1 ELSE 0 END)
set #months = MONTH(#today - #thisYearBirthDay) - 1
set #days = DAY(#today - #thisYearBirthDay) - 1
return cast(#years as varchar(2)) + ' years,' + cast(#months as varchar(2)) + ' months,' + cast(#days as varchar(3)) + ' days'
end
create procedure getDatedifference
(
#startdate datetime,
#enddate datetime
)
as
begin
declare #monthToShow int
declare #dayToShow int
--set #startdate='01/21/1934'
--set #enddate=getdate()
if (DAY(#startdate) > DAY(#enddate))
begin
set #dayToShow=0
if (month(#startdate) > month(#enddate))
begin
set #monthToShow= (12-month(#startdate)+ month(#enddate)-1)
end
else if (month(#startdate) < month(#enddate))
begin
set #monthToShow= ((month(#enddate)-month(#startdate))-1)
end
else
begin
set #monthToShow= 11
end
-- set #monthToShow= convert(int, DATEDIFF(mm,0,DATEADD(dd,DATEDIFF(dd,0,#enddate)- DATEDIFF(dd,0,#startdate),0)))-((convert(int,FLOOR(DATEDIFF(day, #startdate, #enddate) / 365.25))*12))-1
if(#monthToShow<0)
begin
set #monthToShow=0
end
declare #amonthbefore integer
set #amonthbefore=Month(#enddate)-1
if(#amonthbefore=0)
begin
set #amonthbefore=12
end
if (#amonthbefore in(1,3,5,7,8,10,12))
begin
set #dayToShow=31-DAY(#startdate)+DAY(#enddate)
end
if (#amonthbefore=2)
begin
IF (YEAR( #enddate ) % 4 = 0 AND YEAR( #enddate ) % 100 != 0) OR YEAR( #enddate ) % 400 = 0
begin
set #dayToShow=29-DAY(#startdate)+DAY(#enddate)
end
else
begin
set #dayToShow=28-DAY(#startdate)+DAY(#enddate)
end
end
if (#amonthbefore in (4,6,9,11))
begin
set #dayToShow=30-DAY(#startdate)+DAY(#enddate)
end
end
else
begin
--set #monthToShow=convert(int, DATEDIFF(mm,0,DATEADD(dd,DATEDIFF(dd,0,#enddate)- DATEDIFF(dd,0,#startdate),0)))-((convert(int,FLOOR(DATEDIFF(day, #startdate, #enddate) / 365.25))*12))
if (month(#enddate)< month(#startdate))
begin
set #monthToShow=12+(month(#enddate)-month(#startdate))
end
else
begin
set #monthToShow= (month(#enddate)-month(#startdate))
end
set #dayToShow=DAY(#enddate)-DAY(#startdate)
end
SELECT
FLOOR(DATEDIFF(day, #startdate, #enddate) / 365.25) as [yearToShow],
#monthToShow as monthToShow ,#dayToShow as dayToShow ,
convert(varchar,FLOOR(DATEDIFF(day, #startdate, #enddate) / 365.25)) +' Year ' + convert(varchar,#monthToShow) +' months '+convert(varchar,#dayToShow)+' days ' as age
return
end
I use this Function I modified (the Days part) From #Dane answer: https://stackoverflow.com/a/57720/2097023
CREATE FUNCTION dbo.EdadAMD
(
#FECHA DATETIME
)
RETURNS NVARCHAR(10)
AS
BEGIN
DECLARE
#tmpdate DATETIME
, #years INT
, #months INT
, #days INT
, #EdadAMD NVARCHAR(10);
SELECT #tmpdate = #FECHA;
SELECT #years = DATEDIFF(yy, #tmpdate, GETDATE()) - CASE
WHEN (MONTH(#FECHA) > MONTH(GETDATE()))
OR (
MONTH(#FECHA) = MONTH(GETDATE())
AND DAY(#FECHA) > DAY(GETDATE())
) THEN
1
ELSE
0
END;
SELECT #tmpdate = DATEADD(yy, #years, #tmpdate);
SELECT #months = DATEDIFF(m, #tmpdate, GETDATE()) - CASE
WHEN DAY(#FECHA) > DAY(GETDATE()) THEN
1
ELSE
0
END;
SELECT #tmpdate = DATEADD(m, #months, #tmpdate);
IF MONTH(#FECHA) = MONTH(GETDATE())
AND DAY(#FECHA) > DAY(GETDATE())
SELECT #days =
DAY(EOMONTH(GETDATE(), -1)) - (DAY(#FECHA) - DAY(GETDATE()));
ELSE
SELECT #days = DATEDIFF(d, #tmpdate, GETDATE());
SELECT #EdadAMD = CONCAT(#years, 'a', #months, 'm', #days, 'd');
RETURN #EdadAMD;
END;
GO
It works pretty well.
I've seen the question several times with results outputting Years, Month, Days but never a numeric / decimal result. (At least not one that doesn't round incorrectly).
I welcome feedback on this function. Might not still need a little adjusting.
-- Input to the function is two dates.
-- Output is the numeric number of years between the two dates in Decimal(7,4) format.
-- Output is always always a possitive number.
-- NOTE:Output does not handle if difference is greater than 999.9999
-- Logic is based on three steps.
-- 1) Is the difference less than 1 year (0.5000, 0.3333, 0.6667, ect.)
-- 2) Is the difference exactly a whole number of years (1,2,3, ect.)
-- 3) (Else)...The difference is years and some number of days. (1.5000, 2.3333, 7.6667, ect.)
CREATE Function [dbo].[F_Get_Actual_Age](#pi_date1 datetime,#pi_date2 datetime)
RETURNS Numeric(7,4)
AS
BEGIN
Declare
#l_tmp_date DATETIME
,#l_days1 DECIMAL(9,6)
,#l_days2 DECIMAL(9,6)
,#l_result DECIMAL(10,6)
,#l_years DECIMAL(7,4)
--Check to make sure there is a date for both inputs
IF #pi_date1 IS NOT NULL and #pi_date2 IS NOT NULL
BEGIN
IF #pi_date1 > #pi_date2 --Make sure the "older" date is in #pi_date1
BEGIN
SET #l_tmp_date = #pi_date2
SET #pi_date2 = #Pi_date1
SET #pi_date1 = #l_tmp_date
END
--Check #1 If date1 + 1 year is greater than date2, difference must be less than 1 year
IF DATEADD(YYYY,1,#pi_date1) > #pi_date2
BEGIN
--How many days between the two dates (numerator)
SET #l_days1 = DATEDIFF(dd,#pi_date1, #pi_date2)
--subtract 1 year from date2 and calculate days bewteen it and date2
--This is to get the denominator and accounts for leap year (365 or 366 days)
SET #l_days2 = DATEDIFF(dd,dateadd(yyyy,-1,#pi_date2),#pi_date2)
SET #l_years = #l_days1 / #l_days2 -- Do the math
END
ELSE
--Check #2 Are the dates an exact number of years apart.
--Calculate years bewteen date1 and date2, then add the years to date1, compare dates to see if exactly the same.
IF DATEADD(YYYY,DATEDIFF(YYYY,#pi_date1,#pi_date2),#pi_date1) = #pi_date2
SET #l_years = DATEDIFF(YYYY,#pi_date1, #pi_date2) --AS Years, 'Exactly even Years' AS Msg
ELSE
BEGIN
--Check #3 The rest of the cases.
--Check if datediff, returning years, over or under states the years difference
SET #l_years = DATEDIFF(YYYY,#pi_date1, #pi_date2)
IF DATEADD(YYYY,#l_years,#pi_date1) > #pi_date2
SET #l_years = #l_years -1
--use basicly same logic as in check #1
SET #l_days1 = DATEDIFF(dd,DATEADD(YYYY,#l_years,#pi_date1), #pi_date2)
SET #l_days2 = DATEDIFF(dd,dateadd(yyyy,-1,#pi_date2),#pi_date2)
SET #l_years = #l_years + #l_days1 / #l_days2
--SELECT #l_years AS Years, 'Years Plus' AS Msg
END
END
ELSE
SET #l_years = 0 --If either date was null
RETURN #l_Years --Return the result as decimal(7,4)
END
`
Quite Old question, but I want to share what I have done to calculate age
Declare #BirthDate As DateTime
Set #BirthDate = '1994-11-02'
SELECT DATEDIFF(YEAR,#BirthDate,GETDATE()) - (CASE
WHEN MONTH(#BirthDate)> MONTH(GETDATE()) THEN 1
WHEN MONTH(#BirthDate)= MONTH(GETDATE()) AND DAY(#BirthDate) > DAY(GETDATE()) THEN 1
Else 0 END)
Are you trying to calculate the total days/months/years of an age? do you have a starting date? Or are you trying to dissect it (ex: 24 years, 1 month, 29 days)?
If you have a start date that you're working with, datediff will output the total days/months/years with the following commands:
Select DateDiff(d,'1984-07-12','2008-09-11')
Select DateDiff(m,'1984-07-12','2008-09-11')
Select DateDiff(yyyy,'1984-07-12','2008-09-11')
with the respective outputs being (8827/290/24).
Now, if you wanted to do the dissection method, you'd have to subtract the number of years in days (days - 365*years), and then do further math on that to get the months, etc.
Here is SQL code that gives you the number of years, months, and days since the sysdate.
Enter value for input_birth_date this format(dd_mon_yy). note: input same value(birth date) for years, months & days such as 01-mar-85
select trunc((sysdate -to_date('&input_birth_date_dd_mon_yy'))/365) years,
trunc(mod(( sysdate -to_date('&input_birth_date_dd_mon_yy'))/365,1)*12) months,
trunc((mod((mod((sysdate -to_date('&input_birth_date_dd_mon_yy'))/365,1)*12),1)*30)+1) days
from dual
DateTime values in T-SQL are stored as floats. You can just subtract the dates from each other and you now have a new date that is the timespan between them.
declare #birthdate datetime
set #birthdate = '6/15/1974'
--age in years - short version
print year(getdate() - #birthdate) - year(0)
--age in years - visualization
declare #mindate datetime
declare #span datetime
set #mindate = 0
set #span = getdate() - #birthdate
print #mindate
print #birthdate
print getdate()
print #span
--substract minyear from spanyear to get age in years
print year(#span) - year(#mindate)
print month(#span)
print day(#span)
CREATE FUNCTION DBO.GET_AGE
(
#DATE AS DATETIME
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #YEAR AS VARCHAR(50) = ''
DECLARE #MONTH AS VARCHAR(50) = ''
DECLARE #DAYS AS VARCHAR(50) = ''
DECLARE #RESULT AS VARCHAR(MAX) = ''
SET #YEAR = CONVERT(VARCHAR,(SELECT DATEDIFF(MONTH,CASE WHEN DAY(#DATE) > DAY(GETDATE()) THEN DATEADD(MONTH,1,#DATE) ELSE #DATE END,GETDATE()) / 12 ))
SET #MONTH = CONVERT(VARCHAR,(SELECT DATEDIFF(MONTH,CASE WHEN DAY(#DATE) > DAY(GETDATE()) THEN DATEADD(MONTH,1,#DATE) ELSE #DATE END,GETDATE()) % 12 ))
SET #DAYS = DATEDIFF(DD,DATEADD(MM,CONVERT(INT,CONVERT(INT,#YEAR)*12 + CONVERT(INT,#MONTH)),#DATE),GETDATE())
SET #RESULT = (RIGHT('00' + #YEAR, 2) + ' YEARS ' + RIGHT('00' + #MONTH, 2) + ' MONTHS ' + RIGHT('00' + #DAYS, 2) + ' DAYS')
RETURN #RESULT
END
SELECT DBO.GET_AGE('04/12/1986')
DECLARE #BirthDate datetime, #AgeInMonths int
SET #BirthDate = '10/5/1971'
SET #AgeInMonths -- Determine the age in "months old":
= DATEDIFF(MONTH, #BirthDate, GETDATE()) -- .Get the difference in months
- CASE WHEN DATEPART(DAY,GETDATE()) -- .If today was the 1st to 4th,
< DATEPART(DAY,#BirthDate) -- (or before the birth day of month)
THEN 1 ELSE 0 END -- ... don't count the month.
SELECT #AgeInMonths / 12 as AgeYrs -- Divide by 12 months to get the age in years
,#AgeInMonths % 12 as AgeXtraMonths -- Get the remainder of dividing by 12 months = extra months
,DATEDIFF(DAY -- For the extra days, find the difference between,
,DATEADD(MONTH, #AgeInMonths -- 1. Last Monthly Birthday
, #BirthDate) -- (if birthdays were celebrated monthly)
,GETDATE()) as AgeXtraDays -- 2. Today's date.
For the ones that want to create a calculated column in a table to store the age:
CASE WHEN DateOfBirth< DATEADD(YEAR, (DATEPART(YEAR, GETDATE()) - DATEPART(YEAR, DateOfBirth))*-1, GETDATE())
THEN DATEPART(YEAR, GETDATE()) - DATEPART(YEAR, DateOfBirth)
ELSE DATEPART(YEAR, GETDATE()) - DATEPART(YEAR, DateOfBirth) -1 END
There is an easy way, based on the hours between the two days BUT with the end date truncated.
SELECT CAST(DATEDIFF(hour,Birthdate,CAST(GETDATE() as Date))/8766.0 as INT) AS Age FROM <YourTable>
This one has proven to be extremely accurate and reliable. If it weren't for the inner CAST on the GETDATE() it might flip the birthday a few hours before midnight but, with the CAST, it is dead on with the age changing over at exactly midnight.
Here is how I calculate the age given a birth date and the current date.
select case
when cast(getdate() as date) = cast(dateadd(year, (datediff(year, '1996-09-09', getdate())), '1996-09-09') as date)
then dateDiff(yyyy,'1996-09-09',dateadd(year, 0, getdate()))
else dateDiff(yyyy,'1996-09-09',dateadd(year, -1, getdate()))
end as MemberAge
go
There is another method for calculate age is
See below table
FirstName LastName DOB
sai krishnan 1991-11-04
Harish S A 1998-10-11
For finding age,you can calculate through month
Select datediff(MONTH,DOB,getdate())/12 as dates from [Organization].[Employee]
Result will be
firstname dates
sai 27
Harish 20
I have created a function calculateAge that takes parameter dateOfBirth from outside and then it calculates the age in years, months and days and finally it returns in string format.
CREATE FUNCTION calculateAge(dateOfBirth datetime) RETURNS varchar(40)
BEGIN
set #currentdatetime = CURRENT_TIMESTAMP;
set #years = TIMESTAMPDIFF(YEAR,dateOfBirth,#currentdatetime);
set #months = TIMESTAMPDIFF(MONTH,dateOfBirth,#currentdatetime) - #years*12 ;
set #dayOfBirth = EXTRACT(DAY FROM dateOfBirth);
set #today = EXTRACT(DAY FROM #currentdatetime);
set #days = 0;
if (#today > #dayOfBirth) then
set #days = #today - #dayOfBirth;
else
set #decreaseMonth = DATE_SUB(#currentdatetime, INTERVAL 1 MONTH);
set #days = DATEDIFF(dateOfBirth, #decreaseMonth);
end if;
RETURN concat(concat( concat(#years , "years\n") , concat(#months , "months\n")), concat(#days , "days"));
END
Plenty of solutions have been given already, but I beleive this one to be both easy to understand and reliable, as it will handle leap years as well :
case when datepart(dayofyear, #birth) <= datepart(dayofyear, getdate())
then datepart(year, getdate()) - datepart(year, #birth)
else datepart(year, getdate()) - datepart(year, #birth) - 1
end
The idea is to simply compute the difference in years between the two years (birth and now), and substract 1 if the anniversary has not been reached for the current year.
declare #StartDate datetime = '2016-01-31'
declare #EndDate datetime = '2016-02-01'
SELECT #StartDate AS [StartDate]
,#EndDate AS [EndDate]
,DATEDIFF(Year,#StartDate,#EndDate) - CASE WHEN DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate), #StartDate) > #EndDate THEN 1 ELSE 0 END AS [Years]
,DATEDIFF(Month,(DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate) - CASE WHEN DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate), #StartDate) > #EndDate THEN 1 ELSE 0 END,#StartDate)),#EndDate) - CASE WHEN DATEADD(Month, DATEDIFF(Month,DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate) - CASE WHEN DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate), #StartDate) > #EndDate THEN 1 ELSE 0 END,#StartDate),#EndDate) , #StartDate) > #EndDate THEN 1 ELSE 0 END AS [Months]
,DATEDIFF(Day, DATEADD(Month,DATEDIFF(Month, (DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate) - CASE WHEN DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate), #StartDate) > #EndDate THEN 1 ELSE 0 END,#StartDate)),#EndDate) - CASE WHEN DATEADD(Month, DATEDIFF(Month,DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate) - CASE WHEN DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate), #StartDate) > #EndDate THEN 1 ELSE 0 END,#StartDate),#EndDate) , #StartDate) > #EndDate THEN 1 ELSE 0 END ,DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate) - CASE WHEN DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate), #StartDate) > #EndDate THEN 1 ELSE 0 END,#StartDate)) ,#EndDate) - CASE WHEN DATEADD(Day,DATEDIFF(Day, DATEADD(Month,DATEDIFF(Month, (DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate) - CASE WHEN DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate), #StartDate) > #EndDate THEN 1 ELSE 0 END,#StartDate)),#EndDate) - CASE WHEN DATEADD(Month, DATEDIFF(Month,DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate) - CASE WHEN DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate), #StartDate) > #EndDate THEN 1 ELSE 0 END,#StartDate),#EndDate) , #StartDate) > #EndDate THEN 1 ELSE 0 END ,DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate) - CASE WHEN DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate), #StartDate) > #EndDate THEN 1 ELSE 0 END,#StartDate)) ,#EndDate),DATEADD(Month,DATEDIFF(Month, (DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate) - CASE WHEN DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate), #StartDate) > #EndDate THEN 1 ELSE 0 END,#StartDate)),#EndDate) - CASE WHEN DATEADD(Month, DATEDIFF(Month,DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate) - CASE WHEN DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate), #StartDate) > #EndDate THEN 1 ELSE 0 END,#StartDate),#EndDate) , #StartDate) > #EndDate THEN 1 ELSE 0 END ,DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate) - CASE WHEN DATEADD(Year,DATEDIFF(Year,#StartDate,#EndDate), #StartDate) > #EndDate THEN 1 ELSE 0 END,#StartDate))) > #EndDate THEN 1 ELSE 0 END AS [Days]
select DOB as Birthdate,
YEAR(GETDATE()) as ThisYear,
YEAR(getdate()) - EAR(date1) as Age
from TableName
SELECT DOB AS Birthdate ,
YEAR(GETDATE()) AS ThisYear,
YEAR(getdate()) - YEAR(DOB) AS Age
FROM tableprincejain
DECLARE #DoB AS DATE = '1968-10-24'
DECLARE #cDate AS DATE = CAST('2000-10-23' AS DATE)
SELECT
--Get Year difference
DATEDIFF(YEAR,#DoB,#cDate) -
--Cases where year difference will be augmented
CASE
--If Date of Birth greater than date passed return 0
WHEN YEAR(#DoB) - YEAR(#cDate) >= 0 THEN DATEDIFF(YEAR,#DoB,#cDate)
--If date of birth month less than date passed subtract one year
WHEN MONTH(#DoB) - MONTH(#cDate) > 0 THEN 1
--If date of birth day less than date passed subtract one year
WHEN MONTH(#DoB) - MONTH(#cDate) = 0 AND DAY(#DoB) - DAY(#cDate) > 0 THEN 1
--All cases passed subtract zero
ELSE 0
END
declare #BirthDate datetime
declare #TotalYear int
declare #TotalMonths int
declare #TotalDays int
declare #TotalWeeks int
declare #TotalHours int
declare #TotalMinute int
declare #TotalSecond int
declare #CurrentDtTime datetime
set #BirthDate='1998/01/05 05:04:00' -- Set Your date here
set #TotalYear= FLOOR(DATEDIFF(DAY, #BirthDate, GETDATE()) / 365.25)
set #TotalMonths= FLOOR(DATEDIFF(DAY,DATEADD(year, #TotalYear,#BirthDate),GetDate()) / 30.436875E)
set #TotalDays= FLOOR(DATEDIFF(DAY, DATEADD(month, #TotalMonths,DATEADD(year,
#TotalYear,#BirthDate)), GETDATE()))
set #CurrentDtTime=CONVERT(datetime,CONVERT(varchar(50), DATEPART(year,
GetDate()))+'/' +CONVERT(varchar(50), DATEPART(MONTH, GetDate()))
+'/'+ CONVERT(varchar(50),DATEPART(DAY, GetDate()))+' '
+ CONVERT(varchar(50),DATEPART(HOUR, #BirthDate))+':'+
CONVERT(varchar(50),DATEPART(MINUTE, #BirthDate))+
':'+ CONVERT(varchar(50),DATEPART(Second, #BirthDate)))
set #TotalHours = DATEDIFF(hour, #CurrentDtTime, GETDATE())
if(#TotalHours < 0)
begin
set #TotalHours = DATEDIFF(hour,DATEADD(Day,-1, #CurrentDtTime), GETDATE())
set #TotalDays= #TotalDays -1
end
set #TotalMinute= DATEPART(MINUTE, GETDATE())-DATEPART(MINUTE, #BirthDate)
if(#TotalMinute < 0)
set #TotalMinute = DATEPART(MINUTE, DATEADD(hour,-1,GETDATE()))+(60-DATEPART(MINUTE,
#BirthDate))
set #TotalSecond= DATEPART(Second, GETDATE())-DATEPART(Second, #BirthDate)
Print 'Your age are'+ CHAR(13)
+ CONVERT(varchar(50), #TotalYear)+' Years, ' +
CONVERT(varchar(50),#TotalMonths) +' Months, ' +
CONVERT(varchar(50),#TotalDays)+' Days, ' +
CONVERT(varchar(50),#TotalHours)+' Hours, ' +
CONVERT(varchar(50),#TotalMinute)+' Minutes, ' +
CONVERT(varchar(50),#TotalSecond)+' Seconds. ' +char(13)+
'Your are born at day of week was - ' + CONVERT(varchar(50),DATENAME(dw ,
#BirthDate ))
+char(13)+char(13)+
+'Your Birthdate to till date your '+ CHAR(13)
+'Years - ' + CONVERT(varchar(50), FLOOR(DATEDIFF(DAY, #BirthDate, GETDATE()) /
365.25))
+' , Months - ' + CONVERT(varchar(50),DATEDIFF(MM,#BirthDate,getdate()))
+' , Weeks - ' + CONVERT(varchar(50),DATEDIFF(wk,#BirthDate,getdate()))
+' , Days - ' + CONVERT(varchar(50),DATEDIFF(dd,#BirthDate,getdate()))+char(13)+
+'Hours - ' + CONVERT(varchar(50),DATEDIFF(HH,#BirthDate,getdate()))
+' , Minutes - ' + CONVERT(varchar(50),DATEDIFF(mi,#BirthDate,getdate()))
+' , Seconds - ' + CONVERT(varchar(50),DATEDIFF(ss,#BirthDate,getdate()))
Output
Your age are
22 Years, 0 Months, 2 Days, 11 Hours, 30 Minutes, 16 Seconds.
Your are born at day of week was - Monday
Your Birthdate to till date your
Years - 22 , Months - 264 , Weeks - 1148 , Days - 8037
Hours - 192899 , Minutes - 11573970 , Seconds - 694438216