希望您度过了一个 Restful 复活节。如果您能在以下方面给我建议/帮助,我将不胜感激。 (使用功能/不使用功能)
下面是我的数据集,所需的输出(使用规则中的 DOB 规范派生年龄)
需要您的帮助(请注意,我在 MSSQL 环境中寻找解决方案):-
1. 提出年龄字段。(我尝试了以下脚本,但它没有工作,因为它不够动态,无法包含所有 DOB 规则,我还附上了一个 oracle 脚本,用作供大家引用)
SELECT
[ID],
[DOB],
'age' = DATEDIFF(HOUR,(CONVERT(date,(CASE WHEN ([DOB] like '99/%/%') THEN (REPLACE([DOB],'99','01'))
ELSE [DOB] END),103)),GETDATE())/8766
from [Sample]
样本_数据集
create table Sample (
Id Varchar (50),
DOB Varchar (50))
insert into Sample(Id, DOB)
Values
('38603', '24/02/1969'),
('38605', '22/09/1969'),
('36356', '17/03/1954'),
('36374', '17/05/1975'),
('36441', '17/08/1961'),
('1a', '10/05/9999'),
('1b', '10/99/9999'),
('1c', '99/99/9999'),
('2a', '--/--/1935'),
('2b', '00/00/1935'),
('2c', '88/88/1935'),
('2d', '99/99/1935'),
('3a', '10/--/1935'),
('3b', '10/00/1935'),
('3c', '10/88/1935'),
('3d', '10/99/1935'),
('4a', '--/09/1935'),
('4b', '00/09/1935'),
('4c', '88/09/1935'),
('4d', '99/09/1935')
期望的输出
ID | DOB | Age (As of 05-03-2018; dd-mm-yyyy) 38603 | 24/02/1969 | 49 --Everything is known 38605 | 22/09/1969 | 48 36356 | 17/03/1954 | 63 36374 | 17/05/1975 | 42 36441 | 17/08/1961 | 56 1a | 10/05/9999 |null --unknown year 1b | 10/99/9999 |null 1c | 99/99/9999 |null 2a | --/--/1935 |82 --unknown day and month 2b | 00/00/1935 |82 2c | 88/88/1935 |82 2d | 99/99/1935 |82 3a | 10/--/1935 |82 --unknown month but known year 3b | 10/00/1935 |82 3c | 10/88/1935 |82 3d | 10/99/1935 |82 4a | --/09/1935 |82 --unknown day but known month 4b | 00/09/1935 |82 4c | 88/09/1935 |82 4d | 99/09/1935 |82
Rules:- As you can see in the above 5 scenarios in the comments
- Everything is known (use the stated DOB to calculate the age)
- Unknown Year (put age as null as the year is known)
- Unknown day and month (Use 01/07 for the unknown dd/mm and the stated yyyy)
- Unknown month but known day (Use 07 for the unknown mm and the stated dd/07/yyyy)
- Unknown day but known month (Use 15 for the unknown dd and the stated 15/mm/yyyy)
Solution in Oracle
Creating a function first (Tried replicating this logic in T-SQL but unsuccessful, hence i am here)
create or replace function check_dt(in_date in VARCHAR2, in_format in VARCHAR2 default 'DD/MM/YYYY')
RETURN NUMBER
IS
V_DATE DATE;
V_STATUS INTEGER;
BEGIN
SELECT TO_DATE(in_date,in_format)
INTO V_DATECASE
FROM DUAL;
V_STATUS := 0;
RETURN V_STATUS;
EXCEPTION WHEN OTHERS THEN
V_STATUS := SQLCODE;
RETURN V_STATUS;
END;
select check_dt('11/30/2017') from dual;
select TO_DATE('15/--/9999','DD/MM/YYYY') from dual;
select id, dob,
case when check_dt(dob) = -1843 --not valid month, default it to July (07)
THEN substr(dob,1,2)||'/07'||substr(dob,7,4)
when check_dt(dob) = -01847 -- day of month must between 1 and last day of month
THEN '1/07/'||substr(dob,7,4)
WHEN check_dt(dob) = 0 and to_date(dob,'dd/mm/yyyy') > sysdate
THEN NULL
WHEN check_dt(dob) = -0183 -- date not valid for month
THEN '15/'||substr(dob,4)
ELSE
THEN dob
END New_dob
from SAMPLE;
任何帮助将不胜感激。 非常感谢。
最佳答案
SQL服务器
SELECT id,
CASE WHEN YEAR(GETDATE())-REVERSE(LEFT(REVERSE(DOB), CHARINDEX('/', REVERSE(DOB)) - 1)) > = 0
THEN
YEAR(GETDATE())-REVERSE(LEFT(REVERSE(DOB), CHARINDEX('/', REVERSE(DOB)) - 1))
ELSE
NULL
END AS Age
FROM Sample
你的问题的解决方案
WITH CTE AS
(
SELECT id,
CASE WHEN ISNUMERIC(REVERSE(LEFT(REVERSE(DOB), CHARINDEX('/', REVERSE(DOB)) - 1))) = 1 THEN
REVERSE(LEFT(REVERSE(DOB), CHARINDEX('/', REVERSE(DOB)) - 1))
ELSE
NULL
END
AS Year,
CASE WHEN ISNUMERIC(LEFT(DOB, CHARINDEX('/', DOB) - 1)) = 1 THEN
LEFT(DOB, CHARINDEX('/', DOB) - 1)
ELSE
NULL
END AS DAY,
CASE WHEN ISNUMERIC(SUBSTRING(DOB,CHARINDEX('/',DOB)+1, CHARINDEX('/',DOB,CHARINDEX('/',DOB)+1) -CHARINDEX('/',DOB)-1)) = 1 THEN
CASE WHEN SUBSTRING(DOB,CHARINDEX('/',DOB)+1, CHARINDEX('/',DOB,CHARINDEX('/',DOB)+1) -CHARINDEX('/',DOB)-1) >= 1 AND SUBSTRING(DOB,CHARINDEX('/',DOB)+1, CHARINDEX('/',DOB,CHARINDEX('/',DOB)+1) -CHARINDEX('/',DOB)-1) <= 12 THEN
SUBSTRING(DOB,CHARINDEX('/',DOB)+1, CHARINDEX('/',DOB,CHARINDEX('/',DOB)+1) -CHARINDEX('/',DOB)-1)
ELSE
NULL
END
ELSE
NULL
END AS MONTH
FROM Sample),CTE1 AS
(
SELECT id,
year,
month,
CASE WHEN DAY IS NOT NULL THEN
CASE WHEN DAY >= 1 AND DAY <= DAY(EOMONTH(year+'-'+month+'-01')) THEN
DAY
ELSE
NULL
END
ELSE NULL
END AS Day
FROM CTE
)
,CTE2 AS
(
SELECT id,
CASE WHEN YEAR IS NULL
THEN NULL
ELSE
CASE WHEN DAY IS NULL AND MONTH IS NULL THEN '01/07'
WHEN MONTH IS NULL AND DAY IS NOT NULL THEN CAST(day AS VARCHAR)+'/07'
WHEN MONTH IS NOT NULL AND DAY IS NULL THEN '15/'+CAST(MONTH AS VARCHAR)
ELSE CAST(day AS VARCHAR)+'/'+CAST(MONTH AS VARCHAR)
END
+ '/'+CAST(YEAR AS VARCHAR)
END
AS DOB
FROM CTE1
)
SELECT id,DOB,
CASE WHEN DOB IS NOT NULL
THEN
CASE WHEN DATEDIFF (day, CONVERT(DATE, DOB, 103),CONVERT(DATE,GETDATE(),103)) >=0
THEN FLOOR(DATEDIFF (day, CONVERT(DATE, DOB, 103), CONVERT(DATE,GETDATE(),103)) / 365.2425)
ELSE
NULL
END
ELSE
DOB
END AS Age
FROM CTE2
现场演示
http://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=3714d33cacb02c3fce4f0868c9d0990b
关于sql - 从具有差异的 varchar 列计算年龄,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49625725/