Оптимизация запроса Oracle - PullRequest
3 голосов
/ 21 марта 2010
SELECT MAX(verification_id)
  FROM VERIFICATION_TABLE
 WHERE head = 687422
   AND mbr = 23102
   AND RTRIM(LTRIM(lname)) = '.iq bzw'
   AND  TO_CHAR(dob,'MM/DD/YYYY')= '08/10/2004'
   AND system_code = 'M';

Этот запрос занимает 153 секунды. в VERIFICATION_TABLE есть миллионы строк.

Я думаю, что запрос занимает много времени из-за функций в предложении where. Тем не менее, мне нужно выполнить ltrim rtrim для столбцов, а также дату должна соответствовать в формате MM/DD/YYYY. Как я могу оптимизировать этот запрос?

Объяснить план:

SELECT STATEMENT, GOAL = ALL_ROWS           80604   1   59
 SORT AGGREGATE                                   1   59
  TABLE ACCESS FULL P181    VERIFICATION_TABLE  80604   1   59

Первичный ключ:

VRFTN_PK    Primary VERIFICATION_ID 

Индексы:

N_VRFTN_IDX2    head, mbr, dob, lname, verification_id
N_VRFTN_IDX3    last_update_date
N_VRFTN_IDX4    mbr, lname, dob, verification_id
N_VRFTN_IDX4    verification_id

Хотя в плане объяснения я не вижу используемых индексов / первичного ключа. это проблема?

Ответы [ 6 ]

3 голосов
/ 21 марта 2010

Попробуйте это:

SELECT MAX(verification_id)
  FROM VERIFICATION_TABLE
 WHERE head = 687422
   AND mbr = 23102
   AND TRIM(lname) = '.iq bzw'
   AND TRUNCATE(dob) = TO_DATE('08/10/2004')
   AND system_code = 'M';

Удалите это TRUNCATE(), если dob не имеет времени на это, судя по всему (Дата рождения?), Это может не произойти. После этого вам нужна работа по индексированию. Если вы будете так много запрашивать в этом стиле, я бы индексировал mbr и head в индексе с двумя столбцами, если бы вы сказали, что означают столбцы, это поможет определить наилучшую индексацию здесь.

2 голосов
/ 21 марта 2010

Единственный индекс, который является возможным кандидатом для использования в вашем запросе, - это N_VRFTN_IDX2, поскольку он индексирует четыре столбца, которые вы используете в предложении WHERE: HEAD, MBR, DOB и LNAME.

Однако, поскольку вы применяете функции как для DOB, так и для LNAME, они не могут рассматриваться. Затем оптимизатор может решить не использовать этот индекс, поскольку он считает, что HEAD + MBR сами по себе являются недостаточно избирательной комбинацией. Если вы удалили вызов TO_CHAR () из DOB, у вас есть три ведущих столбца в N_VRFTN_IDX2, которые могут сделать его более привлекательным для оптимизатора. Точно так же необходимо TRIM () LNAME?

Другое дело, что поиск SYSTEM_CODE означает, что запрос должен считываться из таблицы (поскольку этот столбец не проиндексирован). Если N_VRFTN_IDX2 имеет плохой кластеризационный фактор, оптимизатор может принять решение выполнить FULL TABLE SCAN, потому что индексированные чтения являются накладными расходами. Принимая во внимание, что если бы вы добавили SYSTEM_CODE в индекс, весь запрос мог бы быть удовлетворен с помощью INDEX RANGE SCAN, что было бы намного быстрее.

Наконец, насколько свежа ваша статистика? Если ваша статистика устарела, это может привести к тому, что оптимизатор примет глупое решение. Например, более точная статистика может привести к тому, что оптимизатор будет использовать составной индекс даже с двумя ведущими столбцами.

1 голос
/ 22 марта 2010

Проверьте типы данных для HEAD и MBR. Значения «687422 и 23102» имеют ощущение «избирательности». То есть, если у вас есть сотни тысяч значений для заголовка и миллионы записей в таблице, может показаться, что HEAD довольно избирателен. [Это может быть полностью вводит в заблуждение, хотя.]

В любом случае, вы можете обнаружить, что HEAD и / или MBR на самом деле хранятся как поля VARCHAR2 или CHAR, а не NUMBER. Если это так, сравнение символа с числом предотвратит использование индекса. Попробуйте следующее (и я включил преобразование предиката dob с датой, но добавил явную маску формата).

SELECT MAX(verification_id)
  FROM VERIFICATION_TABLE
 WHERE head = '687422'
   AND mbr = '23102'
   AND RTRIM(LTRIM(lname)) = '.iq bzw'
   AND TRUNCATE(dob) = TO_DATE('08/10/2004','MM/DD/YYYY')
   AND system_code = 'M';
1 голос
/ 21 марта 2010

Вы должны превратить литерал в ДАТУ, а не столбец в VARCHAR2, как это:

AND  dob = TO_DATE('08/10/2004','MM/DD/YYYY')

Или используйте предпочтительный синтаксис даты ANSI:

AND  dob = DATE '2004-08-10'

Если в столбце dob указано время (обычно дата рождения обычно отсутствует, за исключением предположительно в больнице!), Тогда вы можете сделать:

AND  dob >= DATE '2004-08-10' 
AND  dob <  DATE '2004-08-11' 
0 голосов
/ 21 марта 2010

Попробуйте:

SELECT MAX(verification_id)
  FROM VERIFICATION_TABLE
 WHERE head = 687422
   AND mbr = 23102
   AND TRIM(lname) = '.iq bzw'
   AND dob between TO_DATE('08/10/2004') and TO_DATE('08/11/2004')
   AND system_code = 'M';

Таким образом, будет использоваться возможный индекс для dob.

0 голосов
/ 21 марта 2010

Пожалуйста, предоставьте EXPLAIN вывод для этого запроса, чтобы мы знали, где происходит замедление. Две мысли:

изменить

AND  TO_CHAR(dob,'MM/DD/YYYY')= '08/10/2004'

до

AND  dob = <date here, not sure which oracle str2date function you need>

и использовать индекс на основе функции

RTRIM(LTRIM(lname)) 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...