SQL中的有效日期验证
我有一列以(SQL中的有效日期验证,sql,db2,Sql,Db2,我有一列以(yyyymmdd)的格式保存LossDate。我需要核实,如果该月在30天内结束,则在损失日期为31天的情况下未报告任何损失。我有数百万张唱片。我们将非常感谢您的帮助 LossDate -------- 20120128 20150520 20180631 查询应返回最后一条无效记录,因为200806将在30天后结束。您可以创建一个ISDATE函数,如下所示,然后在查询中使用它,如SELECT*FROM TABLE WHERE DB_IS_DATE(LOSSDATE)=0,以查找所
yyyymmdd
)的格式保存LossDate
。我需要核实,如果该月在30天内结束,则在损失日期为31天的情况下未报告任何损失。我有数百万张唱片。我们将非常感谢您的帮助
LossDate
--------
20120128
20150520
20180631
查询应返回最后一条无效记录,因为200806将在30天后结束。您可以创建一个ISDATE函数,如下所示,然后在查询中使用它,如
SELECT*FROM TABLE WHERE DB_IS_DATE(LOSSDATE)=0
,以查找所有无效日期
CREATE OR REPLACE FUNCTION IS_DATE(YYYYMMDD INTEGER)
RETURNS SMALLINT
LANGUAGE SQL CONTAINS SQL DETERMINISTIC NO EXTERNAL ACTION
RETURN
CASE
WHEN YYYYMMDD/10000 BETWEEN 1 AND 9999
AND( ( MOD(YYYYMMDD/100, 100) IN (1,3,5,7,8,10,12) AND MOD(YYYYMMDD,100) BETWEEN 1 AND 31 ) -- Jan,Mar,May,Jul,Aug,Oct,Dec have 31 days
OR ( MOD(YYYYMMDD/100, 100) IN (4,6,9,11) AND MOD(YYYYMMDD,100) BETWEEN 1 AND 30 ) -- Apr,Jun,Sep,Nov have 30 days
OR ( MOD(YYYYMMDD/100, 100) = 2 AND MOD(YYYYMMDD,100) BETWEEN 1 AND 28 ) -- Feb has 28 days
OR ( MOD(YYYYMMDD/100, 100) = 2 AND MOD(YYYYMMDD,100) BETWEEN 1 AND 29 -- unless is a leap year. i.e.
AND ( ( MOD(YYYYMMDD/10000,4) = 0 AND MOD(YYYYMMDD/10000,100) <> 0) -- year is divisable by 4 but not 100
OR MOD(YYYYMMDD/10000,400) = 0 ) -- or year is divisable by 400
)
)
THEN 1
ELSE 0
END
如果输入是有效日期,则可以使用该函数将其转换为日期。例如
SELECT i, IS_DATE(i) AS IS_DATE
, CASE WHEN IS_DATE(i) = 1 THEN DATE(TO_DATE(DIGITS(DECIMAL(i,8,0)),'YYYYMMDD')) END AS DATE
FROM TABLE(VALUES(-999),(0),(1),(00010101),(99991231),(20180101),(20180228),(20160229),(20180229),(20000229),(19000229)) as D(i)
返回
I IS_DATE DATE
-------- ------- ----------
-999 0 NULL
0 0 NULL
1 0 NULL
10101 1 0001-01-01
99991231 1 9999-12-31
20180101 1 2018-01-01
20180228 1 2018-02-28
20160229 1 2016-02-29
20180229 0 NULL
20000229 1 2000-02-29
19000229 0 NULL
该函数还将接受“YYYYMMDD”格式的字符串。Db2将为您将字符串转换为整数。
如果您有一个字符串,例如“YYYY-MM-DD”格式,则可以创建调用上述函数的函数的字符版本,例如
CREATE OR REPLACE FUNCTION IS_DATE(YYYYMMDD VARCHAR(10))
RETURNS INTEGER
LANGUAGE SQL CONTAINS SQL DETERMINISTIC NO EXTERNAL ACTION
RETURN
CASE WHEN REGEXP_LIKE(YYYYMMDD,'[0-9]{4}?-[0-9]{2}?-[0-9]{2}?') = 1
THEN IS_DATE(INTEGER(REPLACE(YYYYMMDD,'-',''))) ELSE 0 END
另一种选择是创建一个UDF,该UDF使用内置Db2函数,但捕获它们可能生成的任何错误。例如
CREATE OR REPLACE FUNCTION IS_DATE(input VARCHAR(32), format VARCHAR(32))
RETURNS INTEGER
LANGUAGE SQL CONTAINS SQL DETERMINISTIC ALLOW PARALLEL NO EXTERNAL ACTION
BEGIN
DECLARE CONTINUE HANDLER FOR SQLEXCEPTION, SQLWARNING
RETURN 0;
RETURN CASE WHEN TO_DATE(input, format) >= '0001-01-01' THEN 1 ELSE 0 END;
END
@
对于您的特定问题,以下将返回错误行:
select *
from table
where substr(lossdate,5,2) in ('04','06','09','11')
and substr(lossdate,7,2) > '30'
根据您的输入界面,您可能需要在显示值大于31的31天内重复此操作数月
select *
from table
where substr(lossdate,5,2) in ('01','03','07','08','10','12')
and substr(lossdate,7,2) > '31'
二月有点棘手。从增量开始,识别所有超过29天的2月份条目,您可以立即删除/修复这些条目
select *
from table
where substr(lossdate,5,2) = '02'
and substr(lossdate,7,2) > '29'
然后,您需要对2月日期大于28且不是闰年的其余行重复此操作。在过去的100年中,任何可以被4整除的年份都是闰年,因此您可以用它来识别剩余的年份(假设您更正/删除了已发现的错误条目):
选择*
从桌子上
其中substr(lossdate,5,2)='02'
和mod(substr(lossdate,1,4),4))0
和substr(lossdate,7,2)>'28'
Column数据类型?它是数值型的,在db2DB2中有一个LAST_DAY()
函数(),可以让您确定您的LossDate
是否等于当月的LAST_DAY
。但是因为它是数值的,所以我认为您必须将它转换为实际日期或正确表示日期的字符串。DB2对待数字实体不同于字符串实体,因为它与“日期”有关,所以20180631可能与“20180631”不同。可以使用到字符(LossDate)
或时间戳格式(到字符(LossDate),'YYYYMMDD')
。但是为什么“日期”值为20180631
?那不是一个有效的日期?那么,你为什么要在每月最后一天之后的某个日期报告亏损呢?2月份呢?正确,旧系统,数据输入错误,因为没有触发器。因此,我们正在清理无效数据。非常感谢。请注意,如果您正在处理数百万行,我不确定这将如何执行。执行所有mod()和substr()将需要tablescan和一些cpu。为了避免4次不同的扫描,我把WHERE或WHERE条件放在一起(为了避免您的查询将其他查询锁定在外,也许可以使用UR运行它)。我对我的帖子做了一些编辑。上面的一个应该是最终版本(我希望)
select *
from table
where substr(lossdate,5,2) = '02'
and substr(lossdate,7,2) > '29'
select *
from table
where substr(lossdate,5,2) = '02'
and mod(substr(lossdate,1,4),4)) <> 0
and substr(lossdate,7,2) > '28'