使用索引调整查询。最佳方法?

时间:2016-01-14 14:36:33

标签: sql oracle indexing

我在这里遇到问题。我使用的是Oracle 11g,我有这个问题:

SELECT /*+ PARALLEL(16) */
        prdecdde,
         prdenusi,
         prdenpol,
         prdeano,
         prdedtpr
    FROM stat_pro_det
   WHERE prdeisin IS NULL AND PRDENUSI IS NOT NULL AND prdedprv = '20160114'
GROUP BY prdecdde,
         prdenusi,
         prdenpol,
         prdeano,
         prdedtpr;

我得到了下一个执行计划:

--------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name            | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                 | 53229 |  2287K|       |  3652   (4)| 00:00:01 |
|   1 |  HASH GROUP BY               |                 | 53229 |  2287K|  3368K|  3652   (4)| 00:00:01 |
|*  2 |   TABLE ACCESS BY INDEX ROWID| STAT_PRO_DET    | 53229 |  2287K|       |  3012   (3)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN          | STAT_PRO_DET_08 |   214K|       |       |   626   (4)| 00:00:01 |
--------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("PRDENUSI" IS NOT NULL AND "PRDEISIN" IS NULL)
   3 - access("PRDEDPRV"='20160114')

Note
-----
   - Degree of Parallelism is 1 because of hint

我仍然有很多CPU成本。 STAT_PRO_DET_08索引是:

CREATE INDEX STAT_PRO_DET_08 ON STAT_PRO_DET(PRDEDPRV)

我试图将PRDEISIN和PRDENUSI添加到索引中,首先选择最具选择性,但结果最差。

这张表有1.28亿条记录(是的......也许我们需要一个PARTITION TABLE)。但我暂时不能对表格进行分区。

其他吸烟有哪些?一个不同的指数可以获得更好的结果,或者不能做得更好?

提前致谢!!!!

EDIT1: 伙计们......非常感谢你的帮助。特别是@Marmite 我有下一个问题:将这两个查询添加到主题中。为每个索引创建一个索引,或者我可以使用索引来解决这三个查询中的性能问题吗?

  SELECT /*+ PARALLEL(16) */
        prdecdde,
         prdenuau,
         prdenpol,
         prdeano,
         prdedtpr
    FROM stat_pro_det
   WHERE prdeisin IS NULL AND PRDENUSI IS NULL AND prdedprv = '20160114'
GROUP BY prdecdde,
         prdenuau,
         prdenpol,
         prdeano,
         prdedtpr;

SELECT /*+ PARALLEL(16) */
        prdeisin, prdenuau
    FROM stat_pro_det, mtauto
   WHERE prdedprv = '20160114' AND prdenuau = autonuau AND autoisin IS NULL
GROUP BY prdenuau, prdeisin

2 个答案:

答案 0 :(得分:3)

首先,您不妨将查询重写为:

SELECT /*+ PARALLEL(16) */ DISTINCT
        prdecdde, prdenusi, prdenpol, prdeano, prdedtpr
FROM stat_pro_det
WHERE prdeisin IS NULL AND PRDENUSI IS NOT NULL AND prdedprv = '20160114';

(这个更短,可以更容易地更改您感兴趣的列列表。)

此查询的最佳索引是:stat_pro_det(prdedprv, prdeisin, prdenusi, prdecdde, prdenpol, prdeano, prdedtpr)

前三列对WHERE子句很重要,并过滤数据。其余列“覆盖”查询,这意味着索引本身可以解析查询而无需访问数据页。

答案 1 :(得分:2)

首先做出以下决定:

  • 您使用索引访问或使用全表扫描
  • 您使用并行查询或no_parallel

Generall规则是索引访问对于少量访问的记录工作正常,但是在大数量的情况下扩展性不佳。

因此,最好的方法是测试所有选项并查看结果。

对于并行FULL TABLE SCAN

使用如下提示(替换 tab 的表名或别名)

 SELECT /*+ FULL(tab) PARALLEL(16) */

这可以更好地扩展,但对于少量记录来说不是即时的。

索引访问

请注意,此不会并行执行。在问题中查看解释计划中的注释。

定义包含所有列的索引(由Gordon提出),您将执行(顺序)索引范围扫描而无需访问该表。

如上所述 - 根据访问的密钥数量,这将是快速或慢速的。

并行索引访问

您需要定义GLOBAL分区索引

create index tab_idx on tab (col3,col2,col1,col4,col5)
global partition  by hash (col3,col2,col1,col4,col5) PARTITIONS 16;

比提示

 SELECT /*+ INDEX(tab tab_idx) PARALLEL_INDEX(tab,16)  */

您将执行相同的索引范围扫描,但这次是并行执行。所以它有可能会对串行执行产生反应。如果你真的可以打开DOP 16,当然还有你的数据库硬件设置和配置......