函数的子查询替代方案?
Subquery alternative for a function?
我正在尝试获取一个程序,该程序将获取 2 个 varchars(1 个用于名字,1 个用于姓氏),并将 return(如果存在)经理的 ID用那个名字;
我的员工 table 看起来像这样:
EMPLOYEE_ID NUMBER,
FIRST_NAME VARCHAR(50),
LAST_NAME VARCHAR(50),
MANAGER_ID NUMBER
我的功能就在这里:
CREATE OR REPLACE FUNCTION f1_test(v_fn employees.first_name%type,
v_ln employees.last_name%type) RETURN NUMBER IS numar NUMBER;
BEGIN
SELECT e.employee_id INTO numar from
employees e
where e.last_name = v_ln and e.first_name = v_fn;
IF numar IN (SELECT manager_id from employees) /*here is the problem */
THEN DBMS_OUTPUT.PUT_LINE('That is not a manager');
ELSE return numar;
END IF;
return numar;
end;
/
当我 运行 时,我得到这个错误 PLS-00405: subquery not allowed in this context
这个子查询是否有任何替代方法来验证员工的 ID 是否实际上是经理的 ID?
您可以执行第二个查询,例如匹配行的计数,然后测试该结果:
CREATE OR REPLACE FUNCTION f1_test(v_fn employees.first_name%type,
v_ln employees.last_name%type)
RETURN NUMBER IS
numar NUMBER;
manager_count NUMBER;
BEGIN
SELECT e.employee_id INTO numar from
employees e
where e.last_name = v_ln and e.first_name = v_fn;
SELECT count(*) INTO manager_count from
employees e
where manager_id = numar;
IF manager_count = 0
THEN DBMS_OUTPUT.PUT_LINE('That is not a manager');
ELSE return numar;
END IF;
return numar;
END;
/
或者在单个查询中,您可以使用外部联接并计算匹配行,或者 - 我认为更清楚一点,并且可能更有效(不是真的在这里,但在更复杂的情况下)测试 exists
:
CREATE OR REPLACE FUNCTION f1_test(v_fn employees.first_name%type,
v_ln employees.last_name%type)
RETURN NUMBER IS
numar NUMBER;
is_manager VARCHAR2(3);
BEGIN
SELECT e.employee_id,
case when exists (
select null from employees m where m.manager_id = e.employee_id
) then 'yes' else 'no' end
INTO numar, is_manager from
employees e
where e.last_name = v_ln and e.first_name = v_fn;
IF is_manager != 'yes'
THEN DBMS_OUTPUT.PUT_LINE('That is not a manager');
ELSE return numar;
END IF;
return numar;
END;
/
您还应该决定如何处理传递给您的不匹配任何行的参数。目前,查询将在 PL/SQL 上下文中抛出“ORA-01403:未找到数据”,或者在 SQL 上下文中抛出 return null,这可能没问题。
假设调用您函数的人能够看到您通过 dbms_output
发送的任何内容通常是不安全的(默认情况下 db<>fiddle 不会显示 SQL调用,例如);并且您的 else
分支有点毫无意义,因为您 return 无论如何都得到 numar
值。
不过,所有这些都超出了您实际问题的范围。
基于 Alex Poole 的回答,替代方案可能如下。
CREATE OR REPLACE FUNCTION f1_test(v_fn employees.first_name%type,
v_ln employees.last_name%type)
RETURN NUMBER IS
numar NUMBER;
manager_id employees.manager_id%type;
BEGIN
with
"S" as
( select e.employee_id
, m.manager_id
, 1 srt
from employees e
left join employees m
on e.employee_id = m.employee_id
where e.last_name = v_ln
and e.first_name = v_fn
and rownum = 1
union all
select null
, null
, 2
from dual
)
select employee_id
, manager_id
into numar
, manager_id
from "S"
where rownum = 1
order by srt
;
if numar is null then
dbms_output.put_line('That is not an employee');
elsif manager_id is null then
DBMS_OUTPUT.PUT_LINE('That is not a manager');
end if;
return numar;
END;
/
编辑
或者,也许更优雅的解决方案是这个重构版本。
CREATE OR REPLACE FUNCTION f1_test(v_fn employees.first_name%type,
v_ln employees.last_name%type)
RETURN NUMBER IS
numar NUMBER;
manager_id employees.manager_id%type;
BEGIN
select e.employee_id
, m.manager_id
into numar
, manager_id
from dual
left join employees e
on e.last_name = v_ln
and e.first_name = v_fn
left join employees m
on e.employee_id = m.employee_id
where rownum = 1
;
if numar is null then
dbms_output.put_line('That is not an employee');
elsif manager_id is null then
DBMS_OUTPUT.PUT_LINE('That is not a manager');
end if;
return numar;
END;
/
我正在尝试获取一个程序,该程序将获取 2 个 varchars(1 个用于名字,1 个用于姓氏),并将 return(如果存在)经理的 ID用那个名字;
我的员工 table 看起来像这样:
EMPLOYEE_ID NUMBER,
FIRST_NAME VARCHAR(50),
LAST_NAME VARCHAR(50),
MANAGER_ID NUMBER
我的功能就在这里:
CREATE OR REPLACE FUNCTION f1_test(v_fn employees.first_name%type,
v_ln employees.last_name%type) RETURN NUMBER IS numar NUMBER;
BEGIN
SELECT e.employee_id INTO numar from
employees e
where e.last_name = v_ln and e.first_name = v_fn;
IF numar IN (SELECT manager_id from employees) /*here is the problem */
THEN DBMS_OUTPUT.PUT_LINE('That is not a manager');
ELSE return numar;
END IF;
return numar;
end;
/
当我 运行 时,我得到这个错误 PLS-00405: subquery not allowed in this context
这个子查询是否有任何替代方法来验证员工的 ID 是否实际上是经理的 ID?
您可以执行第二个查询,例如匹配行的计数,然后测试该结果:
CREATE OR REPLACE FUNCTION f1_test(v_fn employees.first_name%type,
v_ln employees.last_name%type)
RETURN NUMBER IS
numar NUMBER;
manager_count NUMBER;
BEGIN
SELECT e.employee_id INTO numar from
employees e
where e.last_name = v_ln and e.first_name = v_fn;
SELECT count(*) INTO manager_count from
employees e
where manager_id = numar;
IF manager_count = 0
THEN DBMS_OUTPUT.PUT_LINE('That is not a manager');
ELSE return numar;
END IF;
return numar;
END;
/
或者在单个查询中,您可以使用外部联接并计算匹配行,或者 - 我认为更清楚一点,并且可能更有效(不是真的在这里,但在更复杂的情况下)测试 exists
:
CREATE OR REPLACE FUNCTION f1_test(v_fn employees.first_name%type,
v_ln employees.last_name%type)
RETURN NUMBER IS
numar NUMBER;
is_manager VARCHAR2(3);
BEGIN
SELECT e.employee_id,
case when exists (
select null from employees m where m.manager_id = e.employee_id
) then 'yes' else 'no' end
INTO numar, is_manager from
employees e
where e.last_name = v_ln and e.first_name = v_fn;
IF is_manager != 'yes'
THEN DBMS_OUTPUT.PUT_LINE('That is not a manager');
ELSE return numar;
END IF;
return numar;
END;
/
您还应该决定如何处理传递给您的不匹配任何行的参数。目前,查询将在 PL/SQL 上下文中抛出“ORA-01403:未找到数据”,或者在 SQL 上下文中抛出 return null,这可能没问题。
假设调用您函数的人能够看到您通过 dbms_output
发送的任何内容通常是不安全的(默认情况下 db<>fiddle 不会显示 SQL调用,例如);并且您的 else
分支有点毫无意义,因为您 return 无论如何都得到 numar
值。
不过,所有这些都超出了您实际问题的范围。
基于 Alex Poole 的回答,替代方案可能如下。
CREATE OR REPLACE FUNCTION f1_test(v_fn employees.first_name%type,
v_ln employees.last_name%type)
RETURN NUMBER IS
numar NUMBER;
manager_id employees.manager_id%type;
BEGIN
with
"S" as
( select e.employee_id
, m.manager_id
, 1 srt
from employees e
left join employees m
on e.employee_id = m.employee_id
where e.last_name = v_ln
and e.first_name = v_fn
and rownum = 1
union all
select null
, null
, 2
from dual
)
select employee_id
, manager_id
into numar
, manager_id
from "S"
where rownum = 1
order by srt
;
if numar is null then
dbms_output.put_line('That is not an employee');
elsif manager_id is null then
DBMS_OUTPUT.PUT_LINE('That is not a manager');
end if;
return numar;
END;
/
编辑 或者,也许更优雅的解决方案是这个重构版本。
CREATE OR REPLACE FUNCTION f1_test(v_fn employees.first_name%type,
v_ln employees.last_name%type)
RETURN NUMBER IS
numar NUMBER;
manager_id employees.manager_id%type;
BEGIN
select e.employee_id
, m.manager_id
into numar
, manager_id
from dual
left join employees e
on e.last_name = v_ln
and e.first_name = v_fn
left join employees m
on e.employee_id = m.employee_id
where rownum = 1
;
if numar is null then
dbms_output.put_line('That is not an employee');
elsif manager_id is null then
DBMS_OUTPUT.PUT_LINE('That is not a manager');
end if;
return numar;
END;
/