函数的子查询替代方案?

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;
/

db<>fiddle demo

您还应该决定如何处理传递给您的不匹配任何行的参数。目前,查询将在 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;
/

db<>fiddle demo