我在PL / SQL中编写了一个例程来尝试匹配可能出现印刷/数据输入错误的日期。
它有效,但我想看看是否有人有其他/更好的想法。该例程不需要在PL / SQL中,因为我读了很多语言。
FUNCTION FUZZY_DATE_MATCH(IN_DATE_1 DATE, IN_DATE_2 DATE) RETURN NUMBER AS
MONTH_1 NUMBER(2);
MONTH_2 NUMBER(2);
DAY_1 NUMBER(2);
DAY_2 NUMBER(2);
YEAR_1 NUMBER(4);
YEAR_2 NUMBER(4);
MATCH_SCORE NUMBER(3) := 0;
BEGIN
IF TRUNC(IN_DATE_1) = TRUNC(IN_DATE_2)
THEN
MATCH_SCORE := 100;
ELSE
IF ABS(TRUNC(IN_DATE_1) - TRUNC(IN_DATE_2)) < 2
THEN
MATCH_SCORE :=50;
ELSE
MONTH_1 := TO_NUMBER(TO_CHAR(IN_DATE_1,'MM'));
MONTH_2 := TO_NUMBER(TO_CHAR(IN_DATE_2,'MM'));
IF MONTH_1 = MONTH_2
THEN
MATCH_SCORE := MATCH_SCORE + 15;
ELSE
IF (ABS(MONTH_1 - MONTH_2) < 2) OR
(TO_NUMBER(SUBSTR(LPAD(MONTH_1,2,'0'),2,1)||SUBSTR(LPAD(MONTH_1,2,'0'),1,1)) = MONTH_2)
THEN
MATCH_SCORE := MATCH_SCORE + 7;
END IF;
END IF;
DAY_1 := TO_NUMBER(TO_CHAR(IN_DATE_1,'DD'));
DAY_2 := TO_NUMBER(TO_CHAR(IN_DATE_2,'DD'));
IF DAY_1 = DAY_2
THEN
MATCH_SCORE := MATCH_SCORE + 10;
ELSE
IF (ABS(DAY_1 - DAY_2) < 2) OR
(TO_NUMBER(SUBSTR(LPAD(DAY_1,2,'0'),2,1)||SUBSTR(LPAD(DAY_1,2,'0'),1,1)) = DAY_2)
THEN
MATCH_SCORE := MATCH_SCORE + 5;
END IF;
END IF;
YEAR_1 := TO_NUMBER(TO_CHAR(IN_DATE_1,'YYYY'));
YEAR_2 := TO_NUMBER(TO_CHAR(IN_DATE_2,'YYYY'));
IF YEAR_1 = YEAR_2
THEN
MATCH_SCORE := MATCH_SCORE + 25;
ELSE
IF (ABS(YEAR_1 - YEAR_2) < 2) OR
(TO_NUMBER(SUBSTR(LPAD(YEAR_1,2,'0'),4,1)||SUBSTR(LPAD(YEAR_1,2,'0'),3,1)) = TO_NUMBER(SUBSTR(TO_CHAR(YEAR_2),3)))
THEN
MATCH_SCORE := MATCH_SCORE + 12;
END IF;
END IF;
END IF;
END IF;
RETURN MATCH_SCORE;
END FUZZY_DATE_MATCH;
基本概念是比较两个日期并返回0到100之间的值,其中100是完全匹配,0是不匹配。我正在寻找的错误类型是单个数字错误和转置错误。我的假设是,年份比几个月的体重更重,而体重则超过几天。
我尝试使用谷歌搜索模糊日期匹配,但答案通常处理日期之间的距离而不是数据输入错误。
AAll帮助表示赞赏。
保
答案 0 :(得分:1)
无需亲自实施。看一下UTL_MATCH包,它是Oracle的标准部分。这是一个快速摘要:
FUNCTION edit_distance(s1 IN VARCHAR2, s2 IN VARCHAR2)
RETURN pls_integer;
-- Computes the Levenshtein distance between s1 and s2.
FUNCTION jaro_winkler(s1 IN VARCHAR2, s2 IN VARCHAR2)
RETURN binary_double;
-- Similar to Levenshtein distance, but tries to account for mis-typings,
-- character swaps, etc.
FUNCTION edit_distance_similarity(s1 IN VARCHAR2, s2 IN VARCHAR2)
RETURN pls_integer;
-- Similar to Levenshtein distance, but returns an integer from 0 to 100
-- where 0 means no similarity and 100 means the strings are identical.
FUNCTION jaro_winkler_similarity(s1 IN VARCHAR2, s2 IN VARCHAR2)
RETURN pls_integer;
-- Similar to above, but based on Jaro-Winkler.
这是一个简单的例子:
SELECT UTL_MATCH.EDIT_DISTANCE('potato', 'tomato') AS lev,
UTL_MATCH.EDIT_DISTANCE_SIMILARITY('potato', 'tomato') AS lev_sim,
TO_NUMBER(UTL_MATCH.JARO_WINKLER('potato', 'tomato')) AS jw,
UTL_MATCH.JARO_WINKLER_SIMILARITY('potato', 'tomato') jw_sim
FROM DUAL;
听起来像你可以使用JARO_WINKLER_SIMILARITY。将两个日期转换为标准字符串表示形式(例如TO_CHAR(aDate,'DD / MM / YYYY HH24:MI:SS')),然后比较它们。
(顺便说一句,TO_NUMBER
应用于JARO_WINKLER
的结果,因为Oracle在调用JARO_WINKLER时抛出ORA-031115 : unsupported network datatype or representation
,因为它返回BINARY_DOUBLE,这是Oracle界面在Windows上的例程平台似乎无法处理。如果你不能使用该类型为什么有类型????: - )
分享并享受。
答案 1 :(得分:0)
如果您正在纠正数据输入错误,那么加权不同的日期部分可能没有任何优势 - 我假设同样可能的是,当年部分会出现键错误。因此,这是一个模糊的字符串匹配问题,而不是模糊的日期匹配问题。
一组常用的模糊字符串匹配算法是edit distance - Hamming distance很快但假设不正确的字符串不包含任何字符添加/删除(因此它会很好地执行比较“hello”和“gello”,但不比较“hello”和“hhello”),而Levenshtein distance的计算成本更高,但能够解释错误字符串中的字符添加/删除。
答案 2 :(得分:0)
我有相同的搜索,所以我将分享我的日期函数版本 `参见代码DateMatch:
CREATE Function [fn_DateMatch] (@dt1 DateTime, @dt2 DateTime)
RETURNS FLOAT
AS
BEGIN
DECLARE @Result FLOAT, @yyyy1 NUMERIC, @yyyy2 NUMERIC, @mm1 NUMERIC, @mm2 NUMERIC, @dd1 NUMERIC, @dd2 NUMERIC, @threshold NUMERIC
SELECT @Result = 0, @threshold = 85
IF @dt1 = @dt2 SET @Result = 100
IF (@Result < 100) SET @Result = 100-abs(DATEDIFF (DAY,@dt1, @dt2))
IF (@Result < @threshold)
BEGIN
SELECT @yyyy1 = CONVERT (INT, DATEPART (year, @dt1)), @mm1 = CONVERT (INT, DATEPART (month, @dt1)), @dd1 = CONVERT (INT, DATEPART (day, @dt1))
, @yyyy2 = CONVERT (INT, DATEPART (year, @dt2)), @mm2 = CONVERT (INT, DATEPART (month, @dt2)), @dd2 = CONVERT (INT, DATEPART (day, @dt2))
SET @Result = 100-((@yyyy1+@yyyy2)*3.0+(@mm1+@mm2)*3.0+(@dd1+@dd2)*1.0)*3
IF (@Result < @threshold) and @yyyy1=@yyyy2 and @mm1+@dd1=@mm2+@dd2 SET @Result = 90
END
IF (@Result < @threshold)
BEGIN
IF convert(varchar, @dt1, 108) <> '00:00:00' and convert(varchar, @dt2, 108) <> '00:00:00'
BEGIN
SET @Result = 100-((CONVERT(float, dbo.fn_levenshtein (convert(varchar, @dt1, 120), convert(varchar, @dt2, 120)))/CONVERT(float,len(convert(varchar, @dt2, 120))))*100)
END
ELSE
BEGIN
SET @Result = 100-((CONVERT(float, dbo.fn_levenshtein (convert(varchar, @dt1, 112), convert(varchar, @dt2, 112)))/CONVERT(float,len(convert(varchar, @dt2, 112))))*100)
END
END
RETURN @Result
END;`