Age In Years Of Youngest Employee - sql

I have a table called employees and I I need to get the age of the youngest employee in years.
For example if I look at the table the youngest employee is "57 years old"
The columns:
EmployeeID, Lastname, Title, Birthdate, Hiredate, City, Country
The code I was trying was this:
SELECT MAX(birthdate)
FROM employees;
With that I can get the date of birth of the youngest employee, but now I need to somehow compare it with the current date that would be using "sysdate" and then change it to numbers so that it shows that he is 57 years old, but I have not succeeded

You can use:
SELECT TRUNC(MONTHS_BETWEEN(SYSDATE, MAX(birthdate))/12) AS age
FROM employees;
Which, for the sample data:
CREATE TABLE employees ( id, birthdate ) AS
-- First employee will be 20 tomorrow
SELECT 1, ADD_MONTHS(TRUNC(SYSDATE), -20*12) + INTERVAL '1' DAY FROM DUAL UNION ALL
-- Second employee is 25 today
SELECT 2, ADD_MONTHS(TRUNC(SYSDATE), -25*12) FROM DUAL;
Outputs:
AGE
19
fiddle

You can subtract your MAX(birthdate) from SYSDATE - the result is number of days so you should convert days to years.
WITH
tbl AS
(
select
to_date('09-09-1965', 'dd-mm-yyyy') birthdate,
SYSDATE my_sysdate
from dual
)
SELECT FLOOR((my_sysdate - MAX(birthdate))/ 365) "YEARS_OLD" From tbl
YEARS_OLD
-----------
57

If you don't care much about months and days, a simple option is to extract year from sysdate and youngest birthdate and subtract them:
Sample data:
SQL> with employees (employeeid, lastname, birthdate) as
2 (select 1, 'Little', date '2015-08-25' from dual union all --> youngest
3 select 2, 'Foot' , date '2000-11-13' from dual
4 )
Query:
5 select extract(year from sysdate) - extract(year from max(birthdate)) as age
6 from employees;
AGE
----------
8
SQL>

Related

Compare value of the same column in PL SQL between 2 days

I have table called ACCOUNTS which has every day data. I want to compare if there is change in email address between yesterday and today employee wise.
select EMAIL,EMPLOYEE from ACCOUNTS where day='30-DEC-20'; --today's data
select EMAIL,EMPLOYEE from ACCOUNTS where day='29-DEC-20' -- yesterday'data
I have to deal with bulk data sets here and have no clue whatsoever.
Assuming you have one row per day per employee, one method is aggregation:
select employee,
max(case when date = date '2020-12-29' then email end) as email_yesterday,
max(case when date = date '2020-12-30' then email end) as email_today
from accounts
where date in (date '2020-12-29', date '2020-12-30')
group by employee
having min(email) <> max(email);
If you wanted to generalize this to any day:
select employee,
max(case when date = trunc(sysdate) - interval '1' day then email end) as email_yesterday,
max(case when date = trunc(sysdate) then email end) as email_today
from accounts
where date >= trunc(sysdate) - interval '1' day
group by employee
having min(email) <> max(email);
This option compares e-mail addresses between "this" and "previous" days and returns a row if they differ.
Sample data is in a CTE (lines #1 - 9) - you don't type that as you have it in your table.
Query you might be interested in begins at line #11.
SQL> with accounts (email, employee, day) as
2 -- sample data; you already have that in your table
3 (select 'scott#x.com', 'Scott', date '2020-12-01' from dual union all
4 select 'scott#x.com', 'Scott', date '2020-12-02' from dual union all
5 select 'scott#y.com', 'Scott', date '2020-12-04' from dual union all
6 --
7 select 'adams#x.com', 'Adams', date '2020-12-11' from dual union all
8 select 'adams#y.com', 'Adams', date '2020-12-12' from dual
9 ),
10 -- query begins here
11 data as
12 -- fetch "today's" and "previous day's" e-mail addresses
13 (select email todays_email,
14 lag(email) over (partition by employee order by day desc) previous_email,
15 employee,
16 day
17 from accounts
18 )
19 --
20 -- display data where today's and previous day's e-mail adresses differ
21 select employee, day, todays_email, previous_email
22 from data
23 where todays_email <> previous_email
24 order by employee, day;
EMPLO DAY TODAYS_EMAI PREVIOUS_EM
----- ---------- ----------- -----------
Adams 11.12.2020 adams#x.com adams#y.com
Scott 02.12.2020 scott#x.com scott#y.com
SQL>

How to aggregate a measure on a year-month level based on start and end date in SQL?

I have a SQL query that pulls in three columns as below
employee_id start_date end_date hours
123 09-01-2019 09-02-2019 8
123 09-28-2019 10-01-2019 32
I want to rewrite the query so instead of going granular, i just want to know the sum(hrs) an employee has on a year month level like below:
employee_id Year_Month hours
123 201909 32
123 201910 8
The employee has 4 days in September so 4*8=32 and one day in october so 8 hours for the month of October. My issue is when there are start and end dates that cross between adjacent months. I'm not sure how to write a query to get my desired output and I'd really appreciate any help on this
It might be simpler to use a recursive query to generate series of days in each month, then aggregate by month and count:
with
data as (< your existing query here >),
cte (employee_id, dt, max_dt) as (
select employee_id, start_date, end_date from data
union all
select employee_id, dt + 1, max_dt from cte where dt + 1 < max_dt
)
select employee_id, to_char(dt, 'yyyymm') year_months, count(*) * 8 hours
from mytable
group by employee_id, to_char(dt, 'yyyymm')
This assumes 8 hours per day, as explained in your question.

How to show the worked years in SQL with select?

SELECT LAST_NAME
,DEPARTMENT_ID
,ROUND(MONTHS_BETWEEN (SYSDATE, hire_date)) MONTHS_WORKED
FROM EMPLOYEES
WHERE DEPARTMENT_ID = 90
ORDER BY MONTHS_WORKED;
This select is for "hr" schema in Oracle.
The question is how to make it to show the years and months worked?
We could build on your current attempt with months_between. We can divide the result by 12 to get the number of years; the modulo (ie the remainder) represents the number of months:
select
last_name,
department_id,
floor(months_between(sysdate, hire_date)/12) years_worked,
floor(mod(months_between(sysdate, hire_date), 12)) months_worked
from employees
where department_id = 90
order by years_worked, months_worked;

Doing Math with Dates given in the problem

The following is the query and code attached to the query.I am not able to figure out how to use the date 31-dec-2006 in the problem.
For each rental property, list the address, include street, city, state.
Also list rental type and number of days listed as "Number of Days Listed". Order results by rental type ascending and number of days listed descending.
Instead of using today's date to determine days listed, use 31-dec-2006
The issue is that Im not receiving any results for this query. I believe I am doing something wrong in the where statement. Im not sure how to assigne a value to the date.
select rp_street, rp_city, rp_state, rp_type, (rp_datelisted - sysdate) as "Number of Days Listed"
from rentproperty
where sysdate = '31-dec-2006'
order by rp_type asc, "Number of Days Listed" desc;
When working with dates, then work with dates, not strings. '31-dec-2006' is just a string. It looks like a date (to us, humans), Oracle will try to convert it to a date (if it can), but you can never be sure it'll work. For example, it won't work in my database:
SQL> select count(*) From emp where hiredate < '31-dec-2006';
select count(*) From emp where hiredate < '31-dec-2006'
*
ERROR at line 1:
ORA-01858: a non-numeric character was found where a numeric was expected
If I fix several things, it will work:
SQL> alter session set nls_date_language = english;
Session altered.
SQL> alter session set nls_date_format = 'dd-mon-yyyy';
Session altered.
SQL> select count(*) From emp where hiredate < '31-dec-2006';
COUNT(*)
----------
14
SQL>
Therefore, either use a date literal (which always looks like date 'yyyy-mm-dd'), or apply the TO_DATE function to a string, with appropriate format mask, e.g. to_date('31.12.2006', 'dd.mm.yyyy') and your query will always work.
Here's what you could have done (I shortened the column list); the RENTPROPERTY CTE lists some sample data; you need code from line 7 onwards.
SQL> with rentproperty (rp_street, rp_type, rp_datelisted) as
2 (select 'Oak street' , 'Type A', date '2000-01-25' from dual union all
3 select '31st street', 'Type B', date '2001-10-30' from dual union all
4 select 'Elm street' , 'Type B', date '2004-08-25' from dual union all
5 select 'Bee street' , 'Type A', date '2006-11-30' from dual
6 )
7 select rp_street,
8 rp_type,
9 (date '2006-12-31' - rp_datelisted) days_listed
10 from rentproperty
11 where rp_datelisted < date '2006-12-31'
12 order by rp_type asc, days_listed desc;
RP_STREET RP_TYP DAYS_LISTED
----------- ------ -----------
Oak street Type A 2532
Bee street Type A 31
31st street Type B 1888
Elm street Type B 858
SQL>
In oracle, sysdate is the current date, so unless today is 31-dec-2006, you'll never get any results. If you've used "sysdate" as the column name, try putting it in quotes.
sysdate = '31-dec-2006' condtion is false that is why no output!!
this is probably what you need:
select rp_street, rp_city, rp_state, rp_type, (rp_datelisted - sysdate) as "Number of
Days Listed"
from rentproperty
where rp_datelisted= '31-dec-2006'
order by rp_type asc, "Number of Days Listed" desc;
The Oracle sysdate returns date and time. Try trunc(sysdate) to compare similar values.
select rp_street, rp_city, rp_state, rp_type, (rp_datelisted - sysdate) as "Number of Days Listed"
from rentproperty
where trunc(sysdate) = '31-dec-2006'
order by rp_type asc, "Number of Days Listed" desc;

Checking if a Birthday was between a 6 month span which crosses the year break

I am trying to write SQL code (using SQL Developer) that checks if a person had a birthday within the past 6 month insurance term.
This is what my code currently looks like.
SELECT DRIVER_KEY, CASE WHEN BDAY BETWEEN EFFDAY AND EXPDAY THEN 1 ELSE 0 END AS BDAYIND FROM (
SELECT DISTINCT A.DRIVER_KEY
, TO_CHAR(A.BIRTH_DATE,'mm/dd') AS BDAY
, TO_CHAR(SUBSTR(A.EFFECTIVE_DATE_KEY,5,2)||'/'||SUBSTR(A.EFFECTIVE_DATE_KEY,7,2) ) AS EFFDAY
, TO_CHAR(SUBSTR(A.EXPIRATION_DATE_KEY,5,2)||'/'||SUBSTR(A.EXPIRATION_DATE_KEY,7,2) ) AS EXPDAY
FROM DRIVER_TABLE A
);
It works - so long as the term doesn't cross the break in year. However, my code currently says that 01/25 is NOT between 09/19 and 03/19... How do I fix this?
EDIT: As APC pointed out, my solution does not work for leap years. I would normally delete this post, but it was already selected as the answer to the question. I updated my code below to use the year logic from Brian Leach's solution instead of the to_date strings. Please upvote Brian or APC's answers instead.
Here is my create statement with arbitrary dates:
create table DRIVER_TABLE
(
BIRTH_DATE date,
EFFECTIVE_DATE_KEY date,
EXPIRATION_DATE_KEY date
);
insert into DRIVER_TABLE
values(to_date('05/01/1980','MM/DD/YYYY'),
to_date('11/01/2016','MM/DD/YYYY'),
to_date('04/01/2017','MM/DD/YYYY'));
Here is the query:
select case when BirthdayEFFYear between EFFECTIVE_DATE_KEY and EXPIRATION_DATE_KEY
or BirthdayEXPYear between EFFECTIVE_DATE_KEY and EXPIRATION_DATE_KEY
or to_number(EXPIRATION_DATE_KEY - EFFECTIVE_DATE_KEY) / 365 > 1
then 1 else 0 end BDAYIND
from(
select add_months(BIRTH_DATE,12 * (extract(year from EFFECTIVE_DATE_KEY) - extract(year from BIRTH_DATE))) BirthdayEFFYear,
add_months(BIRTH_DATE,12 * (extract(year from EXPIRATION_DATE_KEY) - extract(year from BIRTH_DATE))) BirthdayEXPYear,
EFFECTIVE_DATE_KEY,EXPIRATION_DATE_KEY
from DRIVER_TABLE A
)
SQLFiddle
Compare dates as dates, not as strings.
Apparently EFFECTIVE_DATE_KEY contains the year in the first four characters, and as such the following should give you what you're looking for:
SELECT DRIVER_KEY,
CASE
WHEN BDAY BETWEEN EFFDAY AND EXPDAY THEN 1
ELSE 0
END AS BDAYIND
FROM (SELECT DISTINCT A.DRIVER_KEY,
A.BIRTH_DATE AS BDAY,
TO_DATE(A.EFFECTIVE_DATE_KEY, 'YYYYMMDD') AS EFFDAY,
TO_DATE(A.EXPIRATION_DATE_KEY, 'YYYYMMDD') AS EXPDAY
FROM DRIVER_TABLE A);
Best of luck.
'01/25' is not between '09/19' and '03/19' because between() is never true when the second argument is smaller than the first argument. You fall ito this trap because you're working with strings. It is always easier to work with dates using the DATE datatype.
It looks like your columns effective_date and expiry_date may not be stored as dates but rather a string; unfortunately this is a common data modelling mistake. If so, you need to cast them to DATE first before applying the following.
This solution has a subquery which selects the pertinent columns from driver_table and also calculates each driver's current age in years. The age is used to derive the last birthday, which is then compared in the main query to the bounds of the insurance term. Because we derive an actual date we can use Oracle's standard date arithmetic so the bdayind is calculated correctly.
SQL> with cte as (
2 select driver_key
3 , date_of_birth
4 , trunc(months_between(sysdate, date_of_birth)/12) as age
5 , add_months(date_of_birth, 12 * (trunc(months_between(sysdate, date_of_birth)/12))) as last_birthday
6 , effective_date
7 , expiry_date
8 from driver_table
9 )
10 select driver_key
11 , date_of_birth as dob
12 , age
13 , effective_date as eff_date
14 , expiry_date as exp_date
15 , last_birthday as last_bday
16 , case
17 when last_birthday between effective_date and expiry_date
18 then 1
19 else 0 end as bdayind
20 from cte
21 /
DRIVER_KEY DOB AGE EFF_DATE EXP_DATE LAST_BDAY BDAYIND
---------- --------- ---- --------- --------- --------- ----------
12 02-APR-98 19 01-DEC-16 31-MAY-17 02-APR-17 1
22 02-APR-98 19 01-JAN-17 30-JUN-17 02-APR-17 1
32 02-SEP-98 18 01-DEC-16 31-MAY-17 02-SEP-16 0
42 02-SEP-98 18 01-JAN-17 30-JUN-17 02-SEP-16 0
SQL>
The subquery produces both age and last_birthday just for demonstration purposes. In real life you only need the last_birthday column.
This solution differs slightly from the others in that:
It works for any birthday between any effective and expiration dates
It accounts for leap years
The raw_data is just setting up the dates for the example:
WITH
raw_data
AS
(SELECT DATE '1963-08-03' AS birthday
, DATE '2017-04-01' AS effectiveday
, DATE '2017-10-31' AS expirationday
, 'Billy' AS name
FROM DUAL
UNION ALL
SELECT DATE '1995-03-20' AS birthday
, DATE '2017-04-01' AS effectiveday
, DATE '2017-10-31' AS expirationday
, 'Sue' AS name
FROM DUAL
UNION ALL
SELECT DATE '1997-01-15' AS birthday
, DATE '2016-12-01' AS effectiveday
, DATE '2017-05-31' AS expirationday
, 'Olga' AS name
FROM DUAL),
mod_data
AS
(SELECT raw_data.*
, ADD_MONTHS (
birthday
, (extract(year from effectiveday) - extract (year from birthday)) * 12
)
effectiveanniversary
, ADD_MONTHS (
birthday
, (extract(year from expirationday) - extract (year from birthday)) * 12
)
expirationanniversary
FROM raw_data)
SELECT name, mod_data.birthday, effectiveday, expirationday
, CASE
WHEN effectiveanniversary BETWEEN effectiveday AND expirationday
OR expirationanniversary BETWEEN effectiveday AND expirationday
THEN
1
ELSE
0
END
found_between
FROM mod_data
NAME BIRTHDAY EFFECTIVEDAY EXPIRATIONDAY FOUND_BETWEEN
Billy 1963/08/03 2017/04/01 2017/10/31 1
Sue 1995/03/20 2017/04/01 2017/10/31 0
Olga 1997/01/15 2016/12/01 2017/05/31 1