2015-01-05 27 views
0

我有一些表,并试图获取指定过滤器的记录时的主键,否则我需要我的函数返回NULL值。哪种方法更适合处理PL/SQL中缺失的数据

我能做到这一点无论是这样

function getIdIfExists(pParam number) return number is 
    resultId number; 
begin 
    begin 
    select ID into resultId from mytable where some_condition = pParam and rownum = 1; 
    exception when NO_DATA_FOUND then 
     resultId := NULL; 
    end; 

    return resultId; 
end; 

或者这样说:

function getIdIfExists(pParam number) return number is 
    resultId number := NULL; 
begin 
    for item in (select * into resultId from mytable where some_condition = pParam) loop 
    resultId := item.ID; 
    exit; 
    end loop; 
    return resultId; 
end; 

那么哪一个更好?或者可能有不同的方法?

回答

1

第一个(使用SQL而不是循环)显然更好,因为您正在使用SQL引擎来搜索数据,而不是使用PL/SQL手动检查表中的每一行。使用PL/SQL时的基本规则是,如果你可以在SQL中完成,不要在PL/SQL中完成。

但是,您的第一个示例将需要修改以使用集合函数(如MAX)来确保只有一行返回,否则多行将引发异常。

以下是我会做:

FUNCTION getIdIfExists(p_id NUMBER) RETURN NUMBER 
IS 
    resultId NUMBER; 
BEGIN 
    SELECT MAX(id) 
    INTO  resultId 
    FROM  mytable 
    WHERE some_condition = p_id; 

    RETURN resultId; 
END; 

编辑:我其实误读了第二个例子中,并没有看到,你在那边有一个WHERE条款。鉴于此,第二个示例实际上被错误地处理,因为它没有ORDER BY,因此给定多个匹配,SQL引擎可能会针对多次执行的相同输入返回不同的结果。第一个例子(修改为使用聚合函数)仍然是一个更清晰的方法来实现你想要的。

+0

谢谢,但异常处理一个好主意? – stasal

+0

我会说使用异常处理是好的,毕竟没有行是你将类作为例外事件,因为特定的ID已经提供给函数,从而推断它是存在的。 – XVar

+2

我不明白你为什么需要NO_DATA_FOUND? MAX(...)总是返回一个值,如果select中没有数据与你的查询匹配,则聚合将为NULL。异常块是错误的,只会刺激开发人员 – Falco

1

如果多个行匹配条件,您的第一个示例将引发TOO_MANY_ROWS异常。第二个例子将迭代所有匹配条件的行并返回最后一个的ID。您的选择是您喜欢哪种行为。

分享和享受。

+0

好吧,我编辑我的例子有点,所以这样的情况应该不会发生 – stasal

+0

编辑好的例子实际上引入了一个错误 - 使用ROWNUM是一个非常糟糕的主意,因为你基本上说“给我一个结果,我不在乎哪一个”,因为你没有ORDER BY。 – XVar

1

所以,如果你的问题是,它的SQL风格比看看下面的实验快: 此代码剪断循环10000×三种不同的方式“从双选假的”:

  1. 明确光标
  2. 隐式游标
  3. SELECT ... INTO ...(这其实也是一个隐式游标)

这里是代码:

procedure cursor_style_compare is 
    l_start_time number; 
    l_duration number; 
    l_dummy  dual.dummy%type; 

    cursor c_explicit is 
     select dummy from dual; 
begin 
    -- Explicit Cursors: 
    l_start_time := dbms_utility.get_time; 
    for i in 1 .. 10000 loop 
     for c1 in c_explicit loop 
      null; 
     end loop; 
    end loop; 
    l_duration := dbms_utility.get_time - l_start_time; 
    dbms_output.put_line('Explicit Cursor:  ' || to_char(l_duration)); 

    -- Implicit Cursors (1): 
    l_start_time := dbms_utility.get_time; 
    for i in 1 .. 10000 loop 
     for c1 in (select dummy from dual) loop 
      null; 
     end loop; 
    end loop; 
    l_duration := dbms_utility.get_time - l_start_time; 
    dbms_output.put_line('Implicit Cursor (1): ' || to_char(l_duration)); 

    -- Implicit Cursors (2): 
    l_start_time := dbms_utility.get_time; 
    for i in 1 .. 10000 loop 
     select dummy into l_dummy from dual; 
    end loop; 
    l_duration := dbms_utility.get_time - l_start_time; 
    dbms_output.put_line('Implicit Cursor (2): ' || to_char(l_duration)); 

end; 

如果我在我的本地数据库(甲骨文11.2 SE在Windows服务器上)运行这段代码,结果是:

Explicit Cursor:  43 
Implicit Cursor (1): 41 
Implicit Cursor (2): 31 

所以简单的 “SELECT ... INTO ...” 被如果结果只是单行,则是获取数据的最快方法。这也是甲骨文的官方推荐: Working with Cursors (by Steven Feuerstein)

相关问题