Oracle Join Million Record Table замедляет производительность запросов - PullRequest
0 голосов
/ 06 мая 2018

Мое требование - найти период простоя для каждого клиента. Чтобы сначала найти незанятого клиента, мне нужно получить регистрационная таблица и имеет 1 миллион записей. Чтобы узнать время последней транзакции для каждого клиента, мне нужно присоединитесь к таблице журнала транзакций, в ней 60 миллионов записей. Ниже мой запрос для этого.

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 голосов
/ 06 мая 2018

Я бы попробовал переписать запрос следующим образом:

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 при условии, что все клиенты имеют транзакции.

Это также упрощает арифметику даты. Это связано не столько с производительностью, сколько с удобочитаемостью.

0 голосов
/ 07 мая 2018

Создание индекса покрытия на TXN_DETL(TXNMOBILENUMBER,TXNDT).

В соответствии с планом выполнения, 86% стоимости приходится на полное сканирование таблицы в TXN_DETL. Если во всех соответствующих столбцах есть индекс, Oracle может использовать этот индекс в качестве таблицы-скина. Операция INDEX FAST FULL SCAN может выполняться значительно быстрее, чем TABLE ACCESS FULL.

0 голосов
/ 06 мая 2018

В вашем запросе используются все данные из обеих таблиц, поэтому первым выбором является изменение плана выполнения с помощью 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"(+))

В зависимости от конфигурации вашего HW и памяти время может меняться, но на последнем HW я бы ожидал, что время истечет менее 10 минут.

Вы можете дополнительно ограничить его, используя

а) параллельный запрос

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;  
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...