Converting a string to date in SQL - sql

I am working on a table where I have two cols named birth and death with dates as strings(to be specific character varying).
I want to calculate the age by using those two columns. If any one of the values is missing, i.e. if either birth is missing or death is missing then it should return 'unknown' (By the way in the table that I am working with the missing values are given as None)
When I try to convert them into date either they are giving me an error or they are giving a wrong age.
For Example, let's say these are the birth and death dates respectively:
birth: 0133-01-30T00:53:28+00:53
death: 0193-07-01T00:53:28+00:53
I used the the following command:
CAST(death as date) - CAST(birth as date)
But this is returning ages such as 2210 or just some absurd age
BY THE WAY I AM DOING THIS IN THE JUPYTER NOTEBOOK USING POSTGRESQL

You can use postgresql age function to calculate the age.
Example:
SELECT coalesce(age('0193-07-01T00:53:28+00:53'::timestamp, '0133-01-30T00:53:28+00:53'::timestamp)::text, 'Unknown');
age
------------------------
60 years 5 mons 2 days
(1 row)
To get years:
SELECT coalesce(EXTRACT(years FROM age('0193-07-01T00:53:28+00:53'::timestamp, '0133-01-30T00:53:28+00:53'::timestamp))::text, 'Unknown');
extract
---------
60
(1 row)
SELECT coalesce(EXTRACT(years FROM age('0193-07-01T00:53:28+00:53'::timestamp, null))::text, 'Unknown');
coalesce
----------
Unknown
(1 row)

Related

SQL Query to get the Average age from multiple 'date' values

I'm needing to get a SQL Query which returns the average age of multiple data inputs in my table
Households
User
Dates
1
2002-01-01
2
2004-06-10
I want to grab both User 1 and 2 date of births and return the average age of them.
Managed to get the age from the date of births using
SELECT *, DATE_FORMAT(FROM_DAYS(DATEDIFF(NOW(), Date)), '%Y') + 0 AS age
FROM Households;
I just can't get the rest of it working to then average the ages out.
Assuming that you are running MySQL, as the syntax of your SQL code suggests.
For starts, I would recommend simplifying the age computation. MySQL provides timestampdiff(), which we can use like so:
select user_id, user_date,
timestampdiff(year, user_date, current_date) age
from houshold
user_id
user_date
age
1
2002-01-01
20
2
2004-06-10
18
3
2004-11-10
17
To compute the average age over all rows of the table, we can use aggregate function avg():
select avg(timestampdiff(year, user_date, current_date)) avg_age
from houshold
avg_age
18.3333
Here is a small demo based on your sample data. Note that I renamed the columns so they do not clash with meaningful SQL names.

How to use order by with modified column

There is a Table with a age-column. The Value of this column is a varchar because the age of persons under the age of 1 is saved in months with an additional 'm' (9 Month old -> '9m')
I know that this is generally a bad idea and one should rather persist the date of birth, but in this case the age refers to the age on a specific day in history - and additionally this is part of a lesson and the whole point is learning how to treat "weird" data.
My first idea was to put a leading zero on all ages which are not purely numeric:
SELECT *
FROM db
ORDER BY REPLACE(age, (IF ISNUMERIC(age) age ELSE CONCAT('0', age))) DESC;
However this is not a valid SQL-statement and neither are my other attempts.
The question is: How can I adjust the value used for ORDER BY without altering the db?
Another approach would be to select only the rows with a purely numeric age value and a separate select for the remaining rows order both of them separately and combine them afterwards.
My take on this was the following:
(SELECT name, age
FROM titanic
WHERE ISNUMERIC(age)
ORDER BY age DESC)
UNION
(SELECT name, age
FROM titanic
WHERE NOT ISNUMERIC(age)
ORDER BY age);
This is in fact valid or at least it gives me a result. But in the result I can't really see what happened to the order, it looks like the UNION undos everything.
Thanks in advance, will take any tip or even just the name of the function/method I should look into!
Would this work?
SELECT name, age
FROM titanic
ORDER BY isnumeric(age), age
I would use "Case When" structure with some transformations in the "Order by" to get the total number of months for both types of ages.
Select name, age
From tbl
Where age SIMILAR TO '[1-9][0-9]*' Or
age SIMILAR TO '[1-9][0-2]?m'
Order by Case When age SIMILAR TO '[1-9][0-2]?m' Then Substring(age,1,CHAR_LENGTH(age)-1)::int
When age SIMILAR TO '[1-9][0-9]*' Then age::int * 12 End
First off storing age is a poor idea, you must update every row regularly, in this case at least monthly. Storing it as string turns a poor idea into a terrible idea. Not only do you have the maintenance, it is not straight forward nor is it straight forward to insert it. Instead store date-of-birth as a timestamp or date. You can then quickly get age (via the age) you can then use the extract function on the resulting interval at whatever level is desired. If you absolutely must present age with text indicator for either years/months (or even days) you create a view that derives the appropriate value. (see demo)
create or replace view titanic as
select name "Name"
, case when extract( year from age(dob))::integer > 0 then to_char (extract( year from age(dob)), '999') || ' years'
when extract( month from age(dob))::integer> 0 then to_char (extract( month from age(dob)), '99') || ' months'
else to_char (extract( day from age(dob)),'99') || ' days'
end "Age"
from titanic_tbl;

How to calculate difference of dates in different formats in Snowflake?

I am merging 2 huge tables in Snowflake and I have 2 columns (one on each table):
"Year_birth" and "Exam_date" and the info inside looks like this respectively:
"1918" and "2007-03-13" (NUMBER(38,0) and VARCHAR(256))
I only want to merge the rows where the difference (i.e., age when the exam was made) is ">18" and "<60"
I was playing around with SELECT DATEDIFF(year,Exam_date, Year_birth) with no success.
Any ideas on how would I do it in Snowflake?
Cheers!
You only have a year, so there is not much you can do about the specific day of the year -- you need to deal with approximations.
So, extract the year from the date string (arggh! it should really be a date) and just compare them:
where (left(datestr, 4)::int - yearnum) not between 18 and 60
I would strongly advise you to fix the database and store these values using a proper date datatype.
You will need to convert the integer year into a date before doing a datediff
example:
set YearOfBirth = 1918;
set ExamDate = '2007-03-03'::DATE;
-- select $YearofBirth as YearofBirth, $ExamDate as ExamDate;
select $YearofBirth as YearofBirth,($YearofBirth::TEXT||'-01-01')::DATE as YearofBirthDate, $ExamDate as ExamDate, datediff(year,($YearofBirth::TEXT||'-01-01')::DATE,$ExamDate) as YearsSinceExam;
USE YEARS_DIFF IN WHERE CLAUSE TO FILTER DIFFERENCE BETWEEN 18 & 60
SELECT DATEDIFF( YEAR,'2007-03-03',TO_DATE(2018::CHAR(4),'YYYY')) YEARS_DIFF;

Select range age with unit of measure in same field

I am working on a table where the age of a person is in a string field where it is in the following format: (amount UnitOfMeasurement)
1 year old = 1 y
11 months old = 11 m
5 Days old = 5 d
I am trying to do a search between a range of age. Is is possible to this via a SQL query where it would order the days (d) first, then months (m), and years (y)?
The database is on SQL Server 2008, but the query will probably be done on Access as it is used for a report's record source.
The first thing I'd do in your situation is try to clean up the messy age field, and standardise it. A quick start might be to create a query where you separate the age value and the age unit, by using expressions such as:
age_unit: Right([age], 1)
and
age_value: Val([age])
If you then sort by age_unit and age_value, you will get all ages sorted correctly (under the assumption that an age in days is always less than an age in months, which in turn is always less than an age in years). Note that you must sort by unit first, then value.
If you want to return ages between a certain minimum and maximum, it's not a problem if you're sticking to a single unit, such as all ages between 5 years and 15 years. Just enter "y" as a criteria under the "age_unit" field (assuming you're using the visual query builder here) and enter "Between 5 and 15" under the "age_value" field.
If you're mixing units ("all ages between 6 months and 2 years") it gets a little more complicated. In this case you'd need to do the following:
On one criteria row you'd enter the following values for each field:
age_unit: "m"
age_value: >=6
And then on the next criteria row:
age_unit: "y"
age_value: <=2
This will return all ages having unit "m" and a value >= 6 OR having unit "y" with a value <=2.
Another somewhat simpler solution would be to convert all ages to a standard unit such as years, by doing some simple calculations, e.g. divide "d" unit values by 365.25, and divide "m" unit values by 12. Then create a new field in your table for the new standardised age data.
Your best bet would be to create a new colum with a real DATETIME value in it. You could then write code, such as a CASE statement, to help convert the string into a DATETIME. Once completed, your calculations will become much simpler.
1.This field doesn't has atomic values. This means that your table is not in 1NF.
You should split Age field into 2 columns with atomic values: IntervalType(CHAR(1)... CHECK(IntervalType IN ('d','m','y')) and IntervalValue (INT; 1,2, etc).
So, instead of Table(...,Age) you can use Table(...,IntervalType,IntervalValue) and
SELECT *
,CONVERT(VARCHAR(10),IntervalValue)
+' '+CASE IntervalType WHEN 'd' THEN 'day' WHEN 'm' THEN 'month' WHEN 'y' THEN 'year' END
+CASE WHEN IntervalValue > 1 THEN 's' ELSE '' END
+' old = '
+CONVERT(VARCHAR(10),IntervalValue)
+' '+IntervalType
FROM table
2.How do you sort these two values: 30 d and 1 month ? One month can have from 28 to 31 days.
3.SQL Server solution:
DECLARE #TestData TABLE
(
Age VARCHAR(25) NOT NULL
,IntervalValue AS CONVERT(INT,LEFT(Age,CHARINDEX(' ',Age))) PERSISTED
,IntervalType AS RIGHT(Age,1) PERSISTED
);
INSERT #TestData
VALUES
('1 year old = 1 y')
,('2 years old = 2 y')
,('11 months old = 11 m')
,('30 Days old = 30 d')
,('5 Days old = 5 d');
SELECT *
FROM #TestData a
ORDER BY a.IntervalType, a.IntervalValue;

age from dob in sql

In the sql query, I am getting problem in the where clause.
I have a dob and ssn of people in a table. I need to find the youngest snr. customer. The age of youngest senior customer starts from 55. The data of DOB contains all the dob's of children,parent, senior customers. In the line "where" I have to write a condition that checks if the age is > 55 and has to be smaller amongst the senior customers.Please suggest me some ways .I posted a similar question before, but did nt get any reply that can help me in solving it.
I dont have the age parameter in my table.
SSn DOB
22 1950-2-2
21 1987-3-3
54 1954-4-7
I need to find the ssn corresponding to the age whcih has to be greater than 55 and smaller among above values .
In the script below, the sub-select finds the DOB of the youngest person 55 years of age or over; this is then used to find the corresponding SSN record(s). [This uses SQL Server syntax.]
SELECT yt.*
FROM *yourtable* yt
INNER JOIN
(
SELECT MAX(DOB) AS DOB
FROM *yourtable*
WHERE DATEADD(year, 55, DOB) < getdate()
) maxdob
ON maxdob.DOB = yt.DOB
n.b. you may find more than a single record if there is more than 1 person with the same DOB.
If you want to force this single restriction, add a TOP 1 clause in your SELECT statement.
hth
If you have the DOB then you can easily calculate the age based on the current date:
WHERE DATE_ADD(DOB, INTERVAL 55 YEAR) < NOW()
This will add 55 years to the DOB and if it is greater than the current time, it's true. This would indicate they are at least 55 years of age.