我正在尝试在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块? 是否需要任何数据库配置?
答案 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中受益,帮助优化器学习更好地估计集合中元素的数量。