【问题标题】:Function or Procedure for an IN clauseIN 子句的函数或过程
【发布时间】:2010-10-08 09:22:57
【问题描述】:

我想编写一个函数或过程,可以在另一个过程的 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 来创建主过程使用的临时表?如果是这样,会不会有性能损失?

【问题讨论】:

    标签: oracle ora-01722


    【解决方案1】:

    这是一个使用 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
    

    【讨论】:

    • 当我有来自不同用户的并发访问时,我是否需要担心数据交叉或自动处理?
    • 不,嵌套表数据对每个用户的会话都是私有的。
    【解决方案2】:

    简单的蛮力方法:

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

    最好将 GET_OFFICE_IDS 更改为返回嵌套表并使用类似的东西:

    OFFICE_ID IN (SELECT * FROM TABLE(GET_OFFICE_IDS))
    

    【讨论】:

    • 也许嵌套表是要走的路。我将不得不阅读它们。我用的不多。
    • 使用该 SQL,您将无法在嵌套查询中执行“select *”,因为它会返回多个列。
    • 嵌套“表”是否意味着磁盘访问(即性能损失),还是只是 Oracle 为该集合类型提出的一个术语?
    • “嵌套表”只是一种集合的名称。在这种情况下,它只会在内存中。您还可以将它们存储在表中,在这种情况下,它们将存储在磁盘上。
    【解决方案3】:

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

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

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

    【讨论】:

    • 它更复杂,因为实际的 SELECT 语句大约有五个级别的子查询,并且将在我的整个程序中使用,这就是为什么我想将它放在函数或过程中。
    • 您能否保存查询/视图以检索您想要的记录并执行类似操作 - IN(从 qryRequiredOfficeIDs 中选择 id)?这样您就可以重用程序中其他地方的查询。 HTH
    【解决方案4】:

    您可能可以使用 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));
    

    通常流水线函数执行得非常好。 但是,包含大量条目的子查询的性能并不总是很好。

    【讨论】:

      【解决方案5】:

      编辑:我违反了 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。优化器不是编译器。它不能内联你的函数。只有您可以帮助您的优化人员制定最佳计划。

      【讨论】:

        猜你喜欢
        • 2013-07-29
        • 1970-01-01
        • 2010-09-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-12
        • 2013-07-12
        • 2014-10-29
        相关资源
        最近更新 更多