在IN子句中使用嵌套表的性能 - Oracle

时间:2014-08-01 19:04:46

标签: performance oracle plsql

我正在尝试在PL-SQL块中的IN子句中使用嵌套表。

首先,我定义了一个TYPE:

CREATE OR REPLACE TYPE VARCHAR_ARRAY AS TABLE OF VARCHAR2(32767);

这是我的PL-SQL块使用'BULK COLLECT INTO':

DECLARE
  COL1 VARCHAR2(50) := '123456789';
  N_TBL                VARCHAR_ARRAY := VARCHAR_ARRAY();
  C NUMBER;
BEGIN

    -- Print timestamp
    DBMS_OUTPUT.PUT_LINE('START: ' || TO_CHAR(SYSTIMESTAMP  ,'dd-mm-yyyy hh24:mi:ss.FF'));

    SELECT    COLUMN1
      BULK COLLECT INTO N_TBL
    FROM MY_TABLE
    WHERE     COLUMN1 = COL1;


    SELECT    COUNT(COLUMN1)
      INTO C
    FROM      MY_OTHER_TABLE
    WHERE     COLUMN1 IN (SELECT column_value FROM TABLE(N_TBL));

    -- Print timestamp
    DBMS_OUTPUT.PUT_LINE('ENDED: ' || TO_CHAR(SYSTIMESTAMP  ,'dd-mm-yyyy hh24:mi:ss.FF'));

END;

输出是:

START: 01-08-2014 12:36:14.997
ENDED: 01-08-2014 12:36:17.554

需要超过 2.5秒(确切地说是2.557秒)

现在,如果我用子查询替换嵌套表,如下所示:

DECLARE
  COL1 VARCHAR2(50) := '123456789';
  N_TBL                VARCHAR_ARRAY := VARCHAR_ARRAY();
  C NUMBER;
BEGIN

    -- Print timestamp
    DBMS_OUTPUT.PUT_LINE('START: ' || TO_CHAR(SYSTIMESTAMP  ,'dd-mm-yyyy hh24:mi:ss.FF'));

    SELECT    COUNT(COLUMN1)
      INTO C
    FROM      MY_OTHER_TABLE
    WHERE     COLUMN1 IN (
                          -- Nested table replaced by a subquery
                          SELECT    COLUMN1
                          FROM MY_TABLE
                          WHERE     COLUMN1 = COL1
    );

    -- Print timestamp
    DBMS_OUTPUT.PUT_LINE('ENDED: ' || TO_CHAR(SYSTIMESTAMP  ,'dd-mm-yyyy hh24:mi:ss.FF'));

END;

输出结果为:

START: 01-08-2014 12:36:08.889
ENDED: 01-08-2014 12:36:08.903

只需 14毫秒 ...... !!!

如何增强此PL-SQL块? 是否需要任何数据库配置?

1 个答案:

答案 0 :(得分:1)

这两个查询计划有何不同?

假设它们是,差异很可能是优化器对子查询将返回的行数有合理的估计,因此,能够选择最有效的计划。当您的数据位于嵌套表格中时(我不想在此处的类型声明中使用单词array,因为这意味着您在使用varray时会使用SELECT COUNT(COLUMN1) INTO C FROM MY_OTHER_TABLE WHERE COLUMN1 IN (SELECT /*+ cardinality(t 50) */ column_value FROM TABLE(N_TBL) t); 。但是,Oracle没有关于集合中有多少元素的信息。默认情况下,它会猜测集合的元素数量与数据块的字节数一样多。因此,如果您有8k块,Oracle会猜测您的集合有8192个元素。

假设您的实际查询没有返回接近8192行的任何位置,并且它实际上返回的行数要多得多或少很多,您可以使用cardinality hint让优化程序进行更准确的猜测。例如,如果您的查询通常返回几十行,您可能需要类似

的内容
cardinality

你在{{1}}提示中加入的文字并不需要特别准确,只是接近一般现实。如果行数完全未知,dynamic_sampling提示可以提供帮助。

如果您使用的是Oracle 11g,您也可以从cardinality feedback中受益,帮助优化器学习更好地估计集合中元素的数量。