PL/SQL 函数返回错误结果
PL/SQL Function returning wrong result
我下面有一个 PL/SQL 函数,它在 SQL Navigator 和 SQL Developer 中返回错误结果,但在 SQL Plus 中返回正确答案。
我们有一个脚本 运行 正在执行它并返回错误的答案,因此尝试修复它。任何人都可以看到它有什么问题吗?它适用于大多数人,但我有一些人在 SQL Navigator 和 Developer 中进入并返回 null/nothing。它不会为他们填充 l_end_date,因此不会填充学分。
出于某种原因在 SQL Plus 中工作正常。
create or replace function mis_get_mem_lcr_credits(p_mem_no in number) RETURN number is
--
v_lcr_credit number;
l_mem_no number;
l_start_date date;
l_end_date date;
l_dob date;
l_18th_date date;
--
cursor c1 is
select mem_no, ind_birth_dt
from cd_individual
where mem_no = l_mem_no
and pkg_mem_utils.get_member_age(mem_no,ind_birth_dt) >= 18
and nvl(ind_student_flag,'N') = 'N'
order by mem_no, ind_birth_dt;
--
cursor c2 is
select distinct m_effdt,
m_termdt
from cd$v_member_contracts9 cd1,
cd_member_product_link cd2
where cd1.mem_no = l_mem_no
and cd1.policy_no = cd2.policy_no
and cd1.m_effdt = cd2.mem_product_eff_dt --.2
and (l_18th_date between cd1.m_effdt and cd1.m_termdt OR cd1.m_effdt > l_18th_date)--.3 18 at time of contract effective date
and nvl(cd1.lapsed_to_start,'N') = 'N'
and cd2.product_id not in (14,41,31) -- Exclude No Cover, DentalProtect and HealthProtect
and cd2.product_id NOT IN (select distinct product_id
from cd_product_options
where nvl(allowed_for_lcr,'Y') = 'N')
order by cd1.m_effdt ASC;
--
begin
--
l_mem_no := p_mem_no;
v_lcr_credit := 0;
l_dob := null;
--
for crec in c1 loop
--
l_dob := crec.ind_birth_dt;
--
-- l_18th_date := substr(to_char(l_dob,'DD/MM/YYYY'),0,6)||(substr(to_char(l_dob,'DD/MM/YYYY'),7,4)+18);
if to_char(l_dob) like '29-02%' then
l_18th_date := add_months(to_date(l_dob+1),216 );
else
l_18th_date := add_months(to_date(l_dob), 216);
end if;
--
for crec2 in c2 loop
--
if crec2.m_termdt > sysdate then
--
l_end_date := sysdate;
--
else
--
l_end_date := crec2.m_termdt;
--
end if;
--
if v_lcr_credit = 0 then --earliest contract
--
if l_18th_date between crec2.m_effdt and crec2.m_termdt then
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,l_18th_date);
--
else
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,crec2.m_effdt);
--
end if;
--
else
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,crec2.m_effdt);
--
end if;
--
end loop;
--
end loop;
--
return round(nvl(v_lcr_credit,0));
--
end mis_get_mem_lcr_credits;
/
show errors
spool off
exit
可能是因为
的值不同
NLS_TERRITORY
、NLS_DATE_FORMAT
等在不同环境下。
所以我建议在脚本中明确设置这些值。例如像 EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_TERRITORY=''AMERICA''';
一些参考文献:
永远不要在 DATE
值上使用 to_date()
。
to_date()
将 varchar
转换为 date
。
如果你用 DATE
调用它,日期值将转换为 varchar,然后再转换回它开始的日期 - 并受到两次邪恶的隐式数据类型转换在那个过程中。
变量l_dob
定义为DATE
所以你必须改变
add_months(to_date(l_dob+1),216 );
...
add_months(to_date(l_dob), 216);
到
add_months(l_dob+1,216);
...
add_months(l_dob, 216);
我下面有一个 PL/SQL 函数,它在 SQL Navigator 和 SQL Developer 中返回错误结果,但在 SQL Plus 中返回正确答案。
我们有一个脚本 运行 正在执行它并返回错误的答案,因此尝试修复它。任何人都可以看到它有什么问题吗?它适用于大多数人,但我有一些人在 SQL Navigator 和 Developer 中进入并返回 null/nothing。它不会为他们填充 l_end_date,因此不会填充学分。
出于某种原因在 SQL Plus 中工作正常。
create or replace function mis_get_mem_lcr_credits(p_mem_no in number) RETURN number is
--
v_lcr_credit number;
l_mem_no number;
l_start_date date;
l_end_date date;
l_dob date;
l_18th_date date;
--
cursor c1 is
select mem_no, ind_birth_dt
from cd_individual
where mem_no = l_mem_no
and pkg_mem_utils.get_member_age(mem_no,ind_birth_dt) >= 18
and nvl(ind_student_flag,'N') = 'N'
order by mem_no, ind_birth_dt;
--
cursor c2 is
select distinct m_effdt,
m_termdt
from cd$v_member_contracts9 cd1,
cd_member_product_link cd2
where cd1.mem_no = l_mem_no
and cd1.policy_no = cd2.policy_no
and cd1.m_effdt = cd2.mem_product_eff_dt --.2
and (l_18th_date between cd1.m_effdt and cd1.m_termdt OR cd1.m_effdt > l_18th_date)--.3 18 at time of contract effective date
and nvl(cd1.lapsed_to_start,'N') = 'N'
and cd2.product_id not in (14,41,31) -- Exclude No Cover, DentalProtect and HealthProtect
and cd2.product_id NOT IN (select distinct product_id
from cd_product_options
where nvl(allowed_for_lcr,'Y') = 'N')
order by cd1.m_effdt ASC;
--
begin
--
l_mem_no := p_mem_no;
v_lcr_credit := 0;
l_dob := null;
--
for crec in c1 loop
--
l_dob := crec.ind_birth_dt;
--
-- l_18th_date := substr(to_char(l_dob,'DD/MM/YYYY'),0,6)||(substr(to_char(l_dob,'DD/MM/YYYY'),7,4)+18);
if to_char(l_dob) like '29-02%' then
l_18th_date := add_months(to_date(l_dob+1),216 );
else
l_18th_date := add_months(to_date(l_dob), 216);
end if;
--
for crec2 in c2 loop
--
if crec2.m_termdt > sysdate then
--
l_end_date := sysdate;
--
else
--
l_end_date := crec2.m_termdt;
--
end if;
--
if v_lcr_credit = 0 then --earliest contract
--
if l_18th_date between crec2.m_effdt and crec2.m_termdt then
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,l_18th_date);
--
else
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,crec2.m_effdt);
--
end if;
--
else
--
v_lcr_credit := v_lcr_credit + months_between(l_end_date,crec2.m_effdt);
--
end if;
--
end loop;
--
end loop;
--
return round(nvl(v_lcr_credit,0));
--
end mis_get_mem_lcr_credits;
/
show errors
spool off
exit
可能是因为
的值不同NLS_TERRITORY
、NLS_DATE_FORMAT
等在不同环境下。
所以我建议在脚本中明确设置这些值。例如像 EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_TERRITORY=''AMERICA''';
一些参考文献:
永远不要在 DATE
值上使用 to_date()
。
to_date()
将 varchar
转换为 date
。
如果你用 DATE
调用它,日期值将转换为 varchar,然后再转换回它开始的日期 - 并受到两次邪恶的隐式数据类型转换在那个过程中。
变量l_dob
定义为DATE
所以你必须改变
add_months(to_date(l_dob+1),216 );
...
add_months(to_date(l_dob), 216);
到
add_months(l_dob+1,216);
...
add_months(l_dob, 216);