5

我想编写一个可以在另一个过程的 IN 子句中使用的函数或过程。该函数或过程将返回 ID 号。

主要程序会说类似

SELECT *
FROM EMPLOYEES
WHERE OFFICE_ID IN (GET_OFFICE_IDS);  -- GET_OFFICE_IDS requires no parameters

GET_OFFICE_IDS 返回一个 VARCHAR2,ID 用逗号分隔。当我运行主程序时,我得到一个“ORA-01722:无效号码”错误,这是有道理的,但我不知道我需要从这里去哪里。

我是否需要 GET_OFFICE_IDS 来创建主过程使用的临时表?如果是这样,会不会有性能损失?

4

5 回答 5

6

这是使用 EMP 表的嵌套表解决方案的工作示例:

create type t_ids is table of integer
/

create or replace function get_office_ids return t_ids
is
   l_ids t_ids := t_ids();
   l_idx integer := 0;
begin
   for r in (select empno from emp where deptno=10)
   loop
      l_ids.extend;
      l_idx := l_idx+1;
      l_ids(l_idx) := r.empno;
   end loop;
   return l_ids;
end;
/

select ename from emp where empno in (select * from table(get_office_ids));


ENAME
----------
CLARK
KING
TEST
MILLER
BINNSY
FARMER
于 2009-02-18T17:09:01.783 回答
3

简单的蛮力方法:

WHERE ','||GET_OFFICE_IDS||',' LIKE '%,'||OFFICE_ID||',%'

最好将 GET_OFFICE_IDS 更改为返回嵌套表并使用以下内容:

OFFICE_ID IN (SELECT * FROM TABLE(GET_OFFICE_IDS))
于 2009-02-18T16:38:25.320 回答
2

我不了解 oracle SQL,但是您不能简单地在 IN 子句中添加另一个 select 语句来返回 ID 吗?

SELECT * FROM EMPLOYEES WHERE_ID IN (SELECT ID FROM tbl_X WHERE x=y);

...或者您是否希望做一些更复杂的事情?

于 2009-02-18T16:37:09.347 回答
1

您可以使用 ref_cursor (ref cursor c := 'select '||.... ) 来执行此操作

但是流水线功能非常有效。像这样使用它:

create or replace type type_varchar2 as table of varchar2(100);

create or replace function GET_OFFICE_IDS return TYPE_varchar2 PIPELINED
is
  retval VARCHAR2(100);
begin
  -- put some sql here which results in statements as below
 retval := '135';
 PIPE ROW (retval);
 retval := '110';
 PIPE ROW (retval);
end GET_OFFICE_IDS;


select *
from entries
where id in (SELECT COLUMN_VALUE FROM TABLE(GET_OFFICE_IDS));

通常流水线函数执行得非常好。然而,具有很多条目的子查询并不总是很好。

于 2009-02-18T17:25:47.920 回答
1

编辑:我违反了 SO 的基本规则,我没有回答 OP。由于已经有一个公认的答案,我觉得警告是谨慎的。

通常,混合使用 SQL 和 PL/SQL 是一个非常糟糕的主意。有 2 个独立的代码引擎。有一个 SQL 引擎和一个 PL/SQL 引擎。强制来回切换数千次绝对会降低性能。

我理解程序员为什么要这样做。我得到它。这一切都是封装的、温暖的和模糊的,但它会严重地削弱你。就像大自然一样,它会用它的景象和声音来引诱你,然后它会折断你的脚踝。

甚至像这样愚蠢的事情。

create or replace function my_Date (p_Date in date)
return varchar
as
begin

    return to_char(p_Date, 'yyyy/mm/dd');

end;

杀死你的执行时间。

打开自动跟踪

然后运行这些。

select to_char(created, 'yyyy/mm/dd'), to_char(last_ddl_time, 'yyyy/mm/dd')  from all_objects


select my_date(created), my_Date(last_DDL_TIME) From all_objects

第二个需要两倍的时间来运行。我在 1 秒内得到查询 1 和在 2 秒内得到 2 的答案。

这是一个非常简单的案例……我所做的只是转换值。想象一下,如果您必须按照自己的意愿加入它。这真的是最坏的情况。

现在想想当你在函数中隐藏东西时优化器完全无法做的事情。

当你做一个 IN 时,有时作为一个连接做起来要快得多。如果某些条件为真,优化器将为您执行此操作。它会将 IN 转换为 JOIN。但是因为你把select伪装在了一个函数里面,它就不能再判断条件是否成熟了。你已经强迫优化器做一些次优的事情。

优化器依赖的一个关键统计数据是行数。是一行还是十亿。它从表和索引的统计信息中知道。您的功能没有统计信息。

你可以把它们放在那里,可以暗示基数,我不是说你不能,但是为什么呢?你为什么想要?看起来您正在使用该函数,因为您是一个勤奋的程序员,他一生都被告知要将冗余代码分解为函数。

你脑海中的那些规则,几乎没有一个适用于 SQL。优化器不是编译器。它不能内联你的函数。只有您可以帮助您的优化人员获得最佳计划。

于 2009-02-18T20:17:04.683 回答