如何提高Oracle中动态sql的查询性能

Posted

技术标签:

【中文标题】如何提高Oracle中动态sql的查询性能【英文标题】:How to improve query performance for dynamic sql in Oracle 【发布时间】:2012-11-30 19:02:58 【问题描述】:

我必须从运行时间定义的表中获取数据并根据运行时间定义的列获取数据,我现在使用带有 ref 游标的动态 sql,如下所示。有没有更有效的方法来提高性能?

PROCEDURE check_error(p_table_name IN VARCHAR2
    ,p_keyword  IN VARCHAR2
    ,p_column_name IN VARCHAR2
    ,p_min_num IN NUMBER
    ,p_time_range IN NUMBER
    ,p_file_desc IN VARCHAR2
    )
IS  
   type t_crs is ref cursor;
   v_cur t_crs;

   v_file_name VARCHAR2(100);
   v_date_started DATE;
   v_date_completed DATE;
   v_counter NUMBER := 0;
   v_sql VARCHAR2(500);
   v_num NUMBER :=0;
BEGIN
   v_sql := 'SELECT '||p_column_name||', DATE_STARTED,DATE_COMPLETED FROM '||p_table_name
            || ' WHERE '||p_column_name||' LIKE '''||p_keyword||'%'' AND  DATE_STARTED > :TIME_LIMIT  ORDER BY '||p_column_name;

    OPEN v_cur FOR v_sql USING (sysdate - (p_time_range/1440));
    LOOP
        FETCH v_cur INTO v_file_name,v_date_started,v_date_completed;
        EXIT WHEN v_cur%NOTFOUND; 
        IF v_date_started IS NOT NULL AND v_date_completed IS NULL   
            AND (sysdate - v_date_started)*1440 > p_time_range THEN
                insert_record(co_alert_stuck,v_file_name,p_table_name,0,p_file_desc,p_time_range);               
        END IF;         
    END LOOP;
END;

顺便说一句,这会让它变得更好吗?

v_sql := 'SELECT :COLUMN_NAME1, DATE_STARTED,DATE_COMPLETED FROM :TABLE WHERE :COLUMN_NAME2 LIKE :KEYWORD AND  DATE_STARTED > :TIME_LIMIT  ORDER BY :COLUMN_NAME3';

OPEN v_cur FOR v_sql USING p_column_name,p_table_name,p_column_name,p_keyword||'%',(sysdate - (p_time_range/1440)),p_column_name;

【问题讨论】:

【参考方案1】:

首先,我不确定我是否理解代码在做什么。在您发布的代码中(您可能已将其缩减以简化代码),IF 语句检查 v_date_started IS NOT NULL 是否是多余的,因为在 DATE_STARTED 上有一个 WHERE 子句。它检查(sysdate - v_date_started)*1440 > p_time_range 是否只是DATE_STARTED 列上WHERE 子句的冗余重复。它会检查v_date_completed IS NULL 是否作为您构建的动态SQL 语句中的附加WHERE 子句更有效。将所有检查都集中在一个地方是有意义的,最有效的地方是在 SQL 语句中。

其次,这个查询应该返回多少行,花费的时间在哪里?如果游标可能返回许多行(对于许多行的某些定义),您将通过将BULK COLLECT 从游标执行到集合并修改insert_record 过程以接受和处理集合来获得一些效率。如果时间都花在执行 SQL 语句上,而查询本身只返回几行,那么 PL/SQL 批量操作可能不会使事情变得更高效。如果瓶颈是执行 SQL 语句,您需要希望传入的任何表上都存在适当的索引。如果瓶颈是 insert_record 过程,我们需要知道该过程在做什么才能评论.

第三,如果insert_record 过程(至少主要)只是将您提取的数据插入到不同的表中,那么摆脱所有循环并只生成动态INSERT 语句会更有效.

第四,关于您的编辑,您不能将绑定变量用于表名或列名,因此您提出的语法无效。它不会更有效率,因为它会产生一堆语法错误。

【讨论】:

谢谢大哥,我删除了“v_date_started IS NOT NULL”这个建议不错,我有另一部分代码检查循环后的结果数(使用v_cur%ROWCOUNT,没有把它放在这里),所以我不能把“v_date_completed IS NULL”放在where子句中。行数通常为两千。 insert_record 只是将数据插入到全局临时表中以生成报告。

以上是关于如何提高Oracle中动态sql的查询性能的主要内容,如果未能解决你的问题,请参考以下文章

Oracle动态游标实现动态SQL循环遍历,和静态游标的比较。

动态性能视图

如何提高oracle模糊查询的性能?

如何在 oracle pl/sql 查询中动态获取字段名称?

如何在 sqlplus 中调试 Oracle 动态 sql?

如何使用 Oracle (PL/SQL) 动态 sql 将数据查询到 %rowtype 变量中