Oracle加入百万记录表会降低查询性能

时间:2018-05-06 09:50:28

标签: sql oracle performance

我的要求是找到每个客户的空闲时间。要找到空闲客户,我必须先取出 注册表,它有100万条记录。要找出每个客户的最后交易时间,我必须这样做 加入事务日志表它有6000万条记录。下面是我的查询。

SELECT CUSTOMERNAME,MOBILENUMBER,ACCOUNTNUMBER,
   CUSTOMERID,LASTTXNDATE, 
   FLOOR(SYSDATE - to_date(TO_CHAR(LASTTXNDATE, 'DD/MM/YYYY'),'DD/MM/YYYY')) AS "IDLE DAYS" 
FROM REGN_MAST
LEFT JOIN 
 ( SELECT TXNMOBILENUMBER,MAX(TXNDT) AS LASTTXNDATE 
   FROM TXN_DETL 
   GROUP BY TXNMOBILENUMBER
 ) 
ON MOBILENUMBER=TXNMOBILENUMBER;

explain plan for
SELECT CUSTOMERNAME,MOBILENUMBER,ACCOUNTNUMBER,
   CUSTOMERID,LASTTXNDATE, 
   FLOOR(SYSDATE - to_date(TO_CHAR(LASTTXNDATE, 'DD/MM/YYYY'),'DD/MM/YYYY')) AS "IDLE DAYS" 
FROM REGN_MAST
LEFT JOIN 
 ( SELECT TXNMOBILENUMBER,MAX(TXNDT) AS LASTTXNDATE 
   FROM TXN_DETL 
   GROUP BY TXNMOBILENUMBER
 ) 
ON MOBILENUMBER=TXNMOBILENUMBER;


Plan hash value: 403296370

------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name                      | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     | Pstart| Pstop |
------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                           |  1231K|   102M|       |  1554K  (1)| 05:10:59 |       |       |
|*  1 |  HASH JOIN RIGHT OUTER               |                           |  1231K|   102M|    58M|  1554K  (1)| 05:10:59 |       |       |
|   2 |   VIEW                               |                           |  1565K|    40M|       |  1535K  (1)| 05:07:07 |       |       |
|   3 |    HASH GROUP BY                     |                           |  1565K|    37M|  2792M|  1535K  (1)| 05:07:07 |       |       |
|   4 |     PARTITION RANGE ALL              |                           |    80M|  1926M|       |  1321K  (1)| 04:24:24 |     1 |1048575|
|   5 |      PARTITION HASH ALL              |                           |    80M|  1926M|       |  1321K  (1)| 04:24:24 |     1 |     4 |
|   6 |       TABLE ACCESS FULL              | TXN_DETL                  |    80M|  1926M|       |  1321K  (1)| 04:24:24 |     1 |1048575|
|   7 |   PARTITION RANGE ALL                |                           |  1231K|    70M|       | 12237   (1)| 00:02:27 |     1 |1048575|
|   8 |    PARTITION HASH ALL                |                           |  1231K|    70M|       | 12237   (1)| 00:02:27 |     1 |     4 |
|   9 |     TABLE ACCESS BY LOCAL INDEX ROWID| REGN_MAST                 |  1231K|    70M|       | 12237   (1)| 00:02:27 |     1 |1048575|
|  10 |      BITMAP CONVERSION TO ROWIDS     |                           |       |       |       |            |          |       |       |
|  11 |       BITMAP INDEX FULL SCAN         | IDX_REGN_MAST_7           |       |       |       |            |          |     1 |1048575|
------------------------------------------------------------------------------------------------------------------------------------------

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

   1 - access("MOBILENUMBER"="TXNMOBILENUMBER"(+))

Note
-----
   - dynamic sampling used for this statement (level=11)

------------------------------------------------------------------------------------------------------------------------------------------------

此查询需要超过25分钟。如何提高此查询的性能。

任何帮助都将非常感谢!!!!!!

3 个答案:

答案 0 :(得分:1)

您的查询使用来自两个表的所有数据,因此第一个选择是使用FULL TABLE SCAN来修改执行计划。

请记住FULL TABLE SCAN速度很慢,但从带有INDEX的表中选择所有行要慢得多......

所以你应该按照以下方式处理行动计划:

------------------------------------------------------------------------------------------
| Id  | Operation            | Name      | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |           |  1000K|    60M|       |   176K  (2)| 00:00:07 |
|*  1 |  HASH JOIN OUTER     |           |  1000K|    60M|    41M|   176K  (2)| 00:00:07 |
|   2 |   TABLE ACCESS FULL  | REGN_MAST |  1000K|    29M|       |  1370   (1)| 00:00:01 |
|   3 |   VIEW               |           |  1014K|    30M|       |   170K  (2)| 00:00:07 |
|   4 |    HASH GROUP BY     |           |  1014K|    16M|  1610M|   170K  (2)| 00:00:07 |
|   5 |     TABLE ACCESS FULL| TXN_DETL  |    60M|   972M|       | 49771   (1)| 00:00:02 |
------------------------------------------------------------------------------------------

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

   1 - access("MOBILENUMBER"="TXNMOBILENUMBER"(+))

根据您的硬件和内存配置,时间会有所不同,但在最近的硬件上,我预计时间会超过10分钟。

您可以使用

进一步限制它

a)并行查询

b)保持包含最后交易日期的物化视图

这里我的生成数据测试时间超过5分钟(见下文)。 所以我的建议是删除所有索引或提示FULL并重试。

SQL> set timi on
SQL> set autotrace traceonly
SQL> SELECT CUSTOMERNAME,MOBILENUMBER,ACCOUNTNUMBER,
  2     CUSTOMERID,LASTTXNDATE,
  3     FLOOR(SYSDATE - to_date(TO_CHAR(LASTTXNDATE, 'DD/MM/YYYY'),'DD/MM/YYYY')
) AS "IDLE DAYS"
  4  FROM REGN_MAST
  5  LEFT JOIN
  6   ( SELECT TXNMOBILENUMBER,MAX(TXNDT) AS LASTTXNDATE
  7     FROM TXN_DETL
  8     GROUP BY TXNMOBILENUMBER
  9   )
 10  ON MOBILENUMBER=TXNMOBILENUMBER;

1000000 rows selected.

Elapsed: 00:05:42.23

示例数据

create table REGN_MAST
as 
select 
'Name'||rownum CUSTOMERNAME,'00'||rownum MOBILENUMBER, 99*rownum ACCOUNTNUMBER, rownum CUSTOMERID 
from dual connect by level <= 1000000;

create table TXN_DETL
as 
with cust as (
select 
'00'||rownum TXNMOBILENUMBER 
from dual connect by level <= 1000000),
trans as (
select  DATE'2018-01-01' + rownum  TXNDT
from dual connect by level <= 60)
select TXNMOBILENUMBER, TXNDT
from cust CROSS join trans;  

答案 1 :(得分:0)

我会尝试将查询重写为:

SELECT m.CUSTOMERNAME, m.MOBILENUMBER, m.ACCOUNTNUMBER,
       m.CUSTOMERID, t.TXNDT, 
       FLOOR(SYSDATE - TRUNC(TXNDT)) AS IDLE_DAYS 
FROM REGN_MAST m JOIN 
     TXN_DETL t
     ON m.MOBILENUMBER = t.TXNMOBILENUMBER
WHERE t.TXNDT = (SELECT MAX(t2.TXNDT) FROM TXN_DETL t2 WHERE m.MOBILENUMBER = t2.TXNMOBILENUMBER);

然后,请确保您在TXN_DETL(TXNMOBILENUMBER, TXNDT)上有一个索引以提高效果。

我假设所有客户都有交易,我将LEFT JOIN更改为INNER JOIN

这也简化了日期算术。这与性能相关性低于可读性。

答案 2 :(得分:0)

TXN_DETL(TXNMOBILENUMBER,TXNDT)上创建覆盖索引。

根据执行计划,86%的成本用于TXN_DETL上的全表扫描。如果所有相关列上都有索引,Oracle可以将该索引用作瘦表。 INDEX FAST FULL SCAN操作的运行速度可能比TABLE ACCESS FULL快得多。