查询成本:全局临时表与集合(虚拟数组)
Query cost: Global Temporary Tables vs. Collections (Virtual Arrays)
我有一个查询,其结果存储在 GTT(全局临时 Table)和集合中。
再次从GTT中选取数据,得到一个很小的代价:103。
SELECT
...
FROM my_table_gtt
JOIN table2 ...
JOIN table3 ...
但是当从 GTT 切换到 Collection(VA - Virtual Array)时,成本飙升(78.000),但两者之间的执行时间差异非常小。
SELECT
...
FROM TABLE(CAST(my_table_va as my_table_tt))
JOIN table2 ...
JOIN table3 ...
我的问题是为什么这两种方法的成本差异如此之大?据我所知,GTT 不存储 table 统计信息,那么为什么它返回的成本比 VA 更好?
全局临时 table 可以像任何其他 table 一样拥有统计信息。事实上,它们就像任何其他 table 一样,它们有数据段,只是在临时 table 空间中。
在 11g 中,统计信息是全局的,因此它们有时会导致执行计划出现问题。在 12c 中,它们是基于会话的,因此每个会话都有适当的会话(如果可用)。
集合类型基数基于 DB 块大小,默认 8 kB 块为 8168。集合内容存储在 PGA 中。在复杂查询中使用集合类型来提示优化器时,提示基数是很常见的。您还可以使用扩展优化器接口来实现自己的成本计算方式。
编辑 - 添加测试:
CREATE TYPE STRINGTABLE IS TABLE OF VARCHAR2(255);
CREATE GLOBAL TEMPORARY TABLE TMP (VALUE VARCHAR2(255));
INSERT INTO TMP SELECT 'Value' || LEVEL FROM DUAL CONNECT BY LEVEL <= 1000000;
DECLARE
x STRINGTABLE;
cnt NUMBER;
BEGIN
SELECT VALUE BULK COLLECT INTO x FROM TMP;
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SYSTIMESTAMP, 'MI:SS.FF3'));
SELECT SUM(LENGTH(VALUE)) INTO cnt FROM TMP;
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SYSTIMESTAMP, 'MI:SS.FF3'));
SELECT SUM(LENGTH(COLUMN_VALUE)) INTO cnt FROM TABLE(x);
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SYSTIMESTAMP, 'MI:SS.FF3'));
END;
在这种情况下,访问 GTT 的速度大约是收集速度的两倍,大约 200 毫秒,而在我的测试机器上为 400 毫秒。当我将行数增加到 10 000 000 时,我得到了 ORA-22813:操作数值超出了第二个查询的系统限制。
集合和 GTT 在 SQL 中最重要的区别是 CBO(基于成本的优化器)对 TABLE 函数(kokbf$...)有限制,例如 JPPD 没有' 使用 TABLE() 函数。
一些解决方法:http://orasql.org/2019/05/30/workarounds-for-jppd-with-view-and-tablekokbf-xmltable-or-json_table-functions/
我有一个查询,其结果存储在 GTT(全局临时 Table)和集合中。
再次从GTT中选取数据,得到一个很小的代价:103。
SELECT
...
FROM my_table_gtt
JOIN table2 ...
JOIN table3 ...
但是当从 GTT 切换到 Collection(VA - Virtual Array)时,成本飙升(78.000),但两者之间的执行时间差异非常小。
SELECT
...
FROM TABLE(CAST(my_table_va as my_table_tt))
JOIN table2 ...
JOIN table3 ...
我的问题是为什么这两种方法的成本差异如此之大?据我所知,GTT 不存储 table 统计信息,那么为什么它返回的成本比 VA 更好?
全局临时 table 可以像任何其他 table 一样拥有统计信息。事实上,它们就像任何其他 table 一样,它们有数据段,只是在临时 table 空间中。
在 11g 中,统计信息是全局的,因此它们有时会导致执行计划出现问题。在 12c 中,它们是基于会话的,因此每个会话都有适当的会话(如果可用)。
集合类型基数基于 DB 块大小,默认 8 kB 块为 8168。集合内容存储在 PGA 中。在复杂查询中使用集合类型来提示优化器时,提示基数是很常见的。您还可以使用扩展优化器接口来实现自己的成本计算方式。
编辑 - 添加测试:
CREATE TYPE STRINGTABLE IS TABLE OF VARCHAR2(255);
CREATE GLOBAL TEMPORARY TABLE TMP (VALUE VARCHAR2(255));
INSERT INTO TMP SELECT 'Value' || LEVEL FROM DUAL CONNECT BY LEVEL <= 1000000;
DECLARE
x STRINGTABLE;
cnt NUMBER;
BEGIN
SELECT VALUE BULK COLLECT INTO x FROM TMP;
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SYSTIMESTAMP, 'MI:SS.FF3'));
SELECT SUM(LENGTH(VALUE)) INTO cnt FROM TMP;
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SYSTIMESTAMP, 'MI:SS.FF3'));
SELECT SUM(LENGTH(COLUMN_VALUE)) INTO cnt FROM TABLE(x);
DBMS_OUTPUT.PUT_LINE(TO_CHAR(SYSTIMESTAMP, 'MI:SS.FF3'));
END;
在这种情况下,访问 GTT 的速度大约是收集速度的两倍,大约 200 毫秒,而在我的测试机器上为 400 毫秒。当我将行数增加到 10 000 000 时,我得到了 ORA-22813:操作数值超出了第二个查询的系统限制。
集合和 GTT 在 SQL 中最重要的区别是 CBO(基于成本的优化器)对 TABLE 函数(kokbf$...)有限制,例如 JPPD 没有' 使用 TABLE() 函数。 一些解决方法:http://orasql.org/2019/05/30/workarounds-for-jppd-with-view-and-tablekokbf-xmltable-or-json_table-functions/