Reputation: 385
I am trying to rewrite a piece of SQL code that was initially written for Vertica. Vertica has the AGE_IN_YEARS()
built-in function which was great to get someone's age. But, now, I need to find a way to have it to work in Snowflake and I did.
An equivalent statement that replaces AGE_IN_YEARS(DateOfBirth)
in Snowflake can be:
case when dateadd(year, datediff(years, DateOfBirth, CURRENT_DATE), DateOfBirth) > CURRENT_DATE
then datediff(years, DateOfBirth, CURRENT_DATE) -1
else datediff(years, DateOfBirth, CURRENT_DATE)
end as AGE
The issue is when I have to use this in a WHERE clause. The original Vertica code is:
SELECT name, dob,
(SELECT AgeRangeCode
FROM AgeRangeTable
WHERE AGE_IN_YEARS(DateOfBirth) BETWEEN MinAge AND MaxAge) AS AgeRangeCode,
CURRENT_DATE as TODAY
from MemberInfoTable
and if i add the AGE_IN_YEARS()
replacement to the above query, I get
SQL compilation error: Unsupported subquery type cannot be evaluated
How do I go about writing this case in the WHERE clause
Upvotes: 0
Views: 9123
Reputation: 25903
not using a function to calculate age, just joining to the data you have like so works:
with age_range_table as (
select * from values (0,16, 'A'),(16,18, 'B'),(18,1000, 'C') v(min_age, max_age, age_range_code)
), member_info_table as (
select name, dob::date as dob from values ('user_80s', '1980-01-01'),('user_90s', '1990-01-01'),('user_10s', '2010-01-01') v(name, dob)
)
select m.name,
m.dob,
a.age_range_code
from member_info_table AS m
join age_range_table AS a
ON m.dob < dateadd('years',-a.min_age, current_date)
AND m.dob >= dateadd('years',-a.max_age, current_date);
giving:
NAME DOB AGE_RANGE_CODE
user_80s 1980-01-01 C
user_90s 1990-01-01 C
user_10s 2010-01-01 A
but that is slightly gross to be doing function calls in a WHERE clause..
with age_range_table as (
select *
from values
(0,16, 'A'),
(16,18, 'B'),
(18,1000, 'C')
v(min_age, max_age, age_range_code)
), member_info_table as (
select name, dob::date as dob
from values
('user_80s', '1980-01-01'),
('user_90s', '1990-01-01'),
('user_10s', '2010-01-01')
v(name, dob)
), today_age_range_table as (
select age_range_code
,dateadd('years',-min_age, current_date) as range_end_date
,dateadd('years',-max_age, current_date) as range_start_date
from age_range_table
)
select m.name,
m.dob,
a.age_range_code
from member_info_table AS m
join today_age_range_table AS a
ON m.dob < a.range_end_date
AND m.dob >= a.range_start_date;
credit note: this is the same solution as Gordon's but with it explicitly shown. And the range logic changed to alter the range times once in a CTE verse altering user.dob for all rows. Based on the assumption you have more users than ranges, and if both are sub 1000 you have no problems ether.
Upvotes: 1
Reputation: 1269443
The issue is when I have to use this in a WHERE clause.
How about adding the age to the dateofbirth
instead?
where dateadd(year, minage, dateofbirth) <= current_date and
dateadd(year, maxage + 1, dateofbirth) > current_date
Upvotes: 2
Reputation: 1319
If you have Age column in your agerange table and you want to compare each customer/participant age with that and also in the min and max range of the age range table then you can simply do it in 2 ways.
SELECT name, dob,
[AgeRangeCode] = (SELECT AgeRangeCode FROM AgeRangeTable WHERE DATEDIFF(Year,mi.dob,CURRENT_DATE) = mi.age
AND DATEDIFF(Year,mi.dob,CURRENT_DATE) BETWEEN MinAge AND MaxAge),
CURRENT_DATE as TODAY
from MemberInfoTable mi
or with a simple join like below
SELECT mi.name, mi.dob, art.AgeRangeCode,
CURRENT_DATE as TODAY
FROM MemberInfoTable mi
JOIN AgeRangeTable art ON art.age = DATEDIFF(Year,mi.dob,CURRENT_DATE)
WHERE DATEDIFF(Year,mi.dob,CURRENT_DATE) BETWEEN art.MinAge AND art.MaxAge
Upvotes: 0