我正在插入格式为
的查询to_date('25-JUN-13','DD-MON-RR')
在 oracle 中,它正常工作,输出为25-JUN-13
在 postgresql 中,同样适用于0001-06-25 BC
。
从 oracle数据库到postgresql 是迁移项目。任何解决方案都适用于 oracle 的情况。
如果我使用的是 DD-MM-YY 格式,那么效果不正常,那么结果就会大不相同。
在POSTGRESQL中运行此查询 - >
select to_char(to_date('25-JUN-53','DD-MON-YY'),'YYYY') as YEAR
ANSWER IS --> 2053
从查询中检索oracle中的相同结果
select to_char(to_date('25-JUN-53','DD-MON-RR'),'YYYY') as YEAR from dual
ASSWER IS --> 1953
当我迁移项目时,Postgresql中应该有相同的功能,以便最终结果应该相同。
答案 0 :(得分:5)
同样不能正常工作......
当然它运作正常。 Per documentation:
如果年份格式规范少于四位数,例如
YYY
, 并且提供的年份少于四位数,年份将是 调整为最接近年份 2020 ,例如95
成为1995年。
因此,70
变为1970,但69
变为2069。
Oracle对格式说明符RR
有不同的规则(Postgres中不存在),基本上年份将调整为最接近年份 2000 (距离当前日期最近的世纪):
我会将功能封装在一个函数中,该函数根据字符串中的年份数来切换世纪。由于Postgres允许function overloading,您甚至可以使用具有不同参数类型的相同函数名to_date()
。
根据上面的文档,Oracle在YY = '50'时回绕,这个函数相当于2049年:
CREATE OR REPLACE FUNCTION to_date(varchar, text)
RETURNS date AS
$func$
SELECT CASE WHEN right($1, 2) > '49' THEN
to_date(left($1, -2) || '19' || right($1, 2), 'DD-MON-YYYY')
ELSE
to_date(left($1, -2) || '20' || right($1, 2), 'DD-MON-YYYY')
END
$func$ LANGUAGE sql STABLE;
仅STABLE
,而非IMMUTABLE
,因为to_date仅为STABLE
。否则你禁用函数内联。
我选择varchar
表示第一个参数与原始参数不同,后者使用text
。
如果年份数是> 49,该函数在转换之前将20世纪(带有'19'),21加入到日期字符串中。第二个参数是忽略。
呼叫:
SELECT to_date('25-JUN-53' , 'DD-MON-YY') AS original
, to_date('25-JUN-53'::varchar, 'DD-MON-YY') AS patched1
, to_date('25-JUN-53'::varchar, 'DD-MON-RR') AS patched2
, to_date('25-JUN-53'::varchar, 'FOO-BAR') AS patched3
我们的自定义函数无论如何都会忽略第二个参数。
结果:
original | patched
------------+-----------
2053-06-25 | 1953-06-25
你可能会让它在2049年之后更加复杂,并考虑第二个参数......
警告:基本功能的功能重载最好小心。如果它留在你的系统中,有人可能会在以后得到令人惊讶的结果。
最好在特殊模式中创建该函数,并有选择地设置search_path
,以便在适当时使用它。在这种情况下,您也可以使用text
作为参数类型:
CREATE SCHEMA specialfunc;
CREATE OR REPLACE FUNCTION specialfunc.to_date(text, text) AS ...
然后:
SET search_path = specialfunc, pg_catalog;
SELECT to_date('25-JUN-53', 'DD-MON-YY') AS patched;
或使用临时功能。参见:
答案 1 :(得分:0)
这是我为解决此问题而编写的示例函数。我自己正在进行同样的迁移。希望它对你有所帮助。你可以使用函数重载来实现,只需将函数重命名为。
CREATE OR REPLACE FUNCTION to_date_rr(TEXT, TEXT)
RETURNS DATE AS
$$
DECLARE
date_v DATE;
fmt text := upper($2);
DATE_VALUE TEXT :=$1;
digit_diff numeric := length($1) - length($2);
BEGIN
$2 = upper($2);
IF substring(fmt from position('RRRR' in fmt) for 4) = 'RRRR' THEN
IF digit_diff < 0 THEN
fmt := replace($2, 'RRRR', 'YYYY');
IF substring(DATE_VALUE from position('RRRR' in $2) for 2) > '50' THEN
date_v := to_date(overlay(DATE_VALUE placing '19' from position('RRRR' in $2) for 0), fmt);
ELSE
date_v := to_date(overlay(DATE_VALUE placing '20' from position('RRRR' in $2) for 0), fmt);
END IF;
ELSE
fmt := replace($2, 'RRRR', 'YYYY');
date_v := to_date($1, fmt);
END IF;
ELSIF substring(fmt from position('RR' in fmt) for 2) = 'RR' THEN
IF digit_diff = 0 THEN
fmt := replace($2, 'RR', 'YY');
IF substring(DATE_VALUE from position('RR' in $2) for 2) > '50' THEN
date_v := to_date(overlay(DATE_VALUE placing '19' from position('RR' in $2) for 0), fmt);
ELSE
date_v := to_date(overlay(DATE_VALUE placing '20' from position('RR' in $2) for 0), fmt);
END IF;
ELSE
fmt := replace($2, 'RR', 'YY');
date_v := to_date($1, fmt);
END IF;
ELSIF substring(fmt from position('YY' in fmt) for 2) = 'YY' and substring(fmt from position('YYYY' in fmt) for 4) != 'YYYY' THEN
IF digit_diff = 0 THEN
IF substring(DATE_VALUE from position('YY' in $2) for 2) >= '00' THEN
date_v := to_date(overlay(DATE_VALUE placing '20' from position('YY' in $2) for 0), fmt);
END IF;
ELSIF digit_diff < 0 THEN
IF substring(DATE_VALUE from position('YY' in $2) for 2) >= '00' THEN
date_v := to_date(overlay(DATE_VALUE placing '200' from position('YY' in $2) for 0), fmt);
END IF;
ELSE
date_v := to_date($1, fmt);
END IF;
ELSE
SELECT to_date($1, $2) INTO date_v;
END IF;
RETURN date_v;
END;
$$
LANGUAGE plpgsql;
答案 2 :(得分:0)
可以使用 to_char(Date Field, 'format to display')