哪种方法更适合处理 PL/SQL 中的缺失数据
Which approach is better for processing missing data in PL/SQL
我有一些 table 并尝试在指定过滤器的记录存在时获取主键,否则我需要我的函数 return NULL 值。
我可以这样做
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;
那么哪个更好呢?或者可能有不同的方法?
如果不止一行符合条件,您的第一个示例将引发 TOO_MANY_ROWS 异常。第二个示例将遍历所有匹配条件的行和 return 最后一行的 ID。您选择喜欢哪种行为。
分享和享受。
第一个(使用 SQL 而不是循环)明显更好,因为您使用 SQL 引擎搜索数据而不是手动检查 [=28] 中的每一行=] 使用 PL/SQL。使用 PL/SQL 时的基本规则是,如果您可以在 SQL 中使用,请不要在 PL/SQL 中使用。
但是,您的第一个示例将需要修改以使用 MAX 等聚合函数来确保只有一行 returned,否则多行将引发异常。
这是我的做法:
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 引擎可以 return 多次执行相同输入的不同结果。第一个示例(修改为使用聚合函数)仍然是一种更简洁的方法来实现您想要的。
因此,如果您的问题是 SQL 的哪种样式比查看以下实验更快:
此代码以三种不同方式截断循环 10000 x "select dummy from dual":
- 显式游标
- 隐式游标
- 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;
如果我 运行 我的本地数据库上的这段代码(Windows 服务器上的 Oracle 11.2 SE),结果是:
Explicit Cursor: 43
Implicit Cursor (1): 41
Implicit Cursor (2): 31
因此,如果结果只是一行,那么简单的 "select ... into ..." 是获取数据的最快方法。这也是Oracle官方的推荐:
Working with Cursors (by Steven Feuerstein)
我有一些 table 并尝试在指定过滤器的记录存在时获取主键,否则我需要我的函数 return NULL 值。
我可以这样做
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;
那么哪个更好呢?或者可能有不同的方法?
如果不止一行符合条件,您的第一个示例将引发 TOO_MANY_ROWS 异常。第二个示例将遍历所有匹配条件的行和 return 最后一行的 ID。您选择喜欢哪种行为。
分享和享受。
第一个(使用 SQL 而不是循环)明显更好,因为您使用 SQL 引擎搜索数据而不是手动检查 [=28] 中的每一行=] 使用 PL/SQL。使用 PL/SQL 时的基本规则是,如果您可以在 SQL 中使用,请不要在 PL/SQL 中使用。
但是,您的第一个示例将需要修改以使用 MAX 等聚合函数来确保只有一行 returned,否则多行将引发异常。
这是我的做法:
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 引擎可以 return 多次执行相同输入的不同结果。第一个示例(修改为使用聚合函数)仍然是一种更简洁的方法来实现您想要的。
因此,如果您的问题是 SQL 的哪种样式比查看以下实验更快: 此代码以三种不同方式截断循环 10000 x "select dummy from dual":
- 显式游标
- 隐式游标
- 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;
如果我 运行 我的本地数据库上的这段代码(Windows 服务器上的 Oracle 11.2 SE),结果是:
Explicit Cursor: 43
Implicit Cursor (1): 41
Implicit Cursor (2): 31
因此,如果结果只是一行,那么简单的 "select ... into ..." 是获取数据的最快方法。这也是Oracle官方的推荐: Working with Cursors (by Steven Feuerstein)