0

나는 당황스러운 상황에 직면했다. 쿼리에는 좋은 실행 계획이 있습니다. 그러나 그 쿼리가 더 큰 쿼리 내에서 내부 쿼리로 사용될 때 그 계획은 변경되었습니다. 왜 그렇게 될지 이해하려고 노력하고 있습니다.더 큰 쿼리의 일부로 실행될 때 내부 쿼리의 실행 계획이 달라짐

이것은 Oracle 11g에 있습니다. 내 쿼리는 그냥 내부 쿼리를 실행하면

SELECT DISTINCT SHIPMENT_KEY 
FROM YFS_SHIPMENT_LINE_H 
WHERE ORDER_HEADER_KEY = '20150113083918815889858' 
OR (ORDER_LINE_KEY IN ( '20150113084438815896336')) 

, 나는 실행 계획을 얻을 :

SELECT * FROM YFS_SHIPMENT_H  
WHERE SHIPMENT_KEY IN 
    (
     SELECT DISTINCT SHIPMENT_KEY 
     FROM YFS_SHIPMENT_LINE_H 
     WHERE ORDER_HEADER_KEY = '20150113083918815889858' 
     OR (ORDER_LINE_KEY IN ( '20150113084438815896336')) 
    ); 

당신이 볼 수 있듯이이있는 내부 쿼리 여기,이 같이

PLAN_TABLE_OUTPUT 
======================================================================================================== 
SQL_ID 3v82m4j5tv1k3, child number 0 
===================================== 
SELECT DISTINCT SHIPMENT_KEY FROM YFS_SHIPMENT_LINE_H WHERE 
ORDER_HEADER_KEY = '20150113083918815889858' OR (ORDER_LINE_KEY IN (
'20150113084438815896336')) 

Plan hash value: 3691773903 

======================================================================================================== 
| Id | Operation      | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
======================================================================================================== 
| 0 | SELECT STATEMENT    |      |  |  | 10 (100)|   | 
| 1 | HASH UNIQUE     |      |  7 | 525 | 10 (10)| 00:00:01 | 
| 2 | CONCATENATION    |      |  |  |   |   | 
| 3 | TABLE ACCESS BY INDEX ROWID| YFS_SHIPMENT_LINE_H |  1 | 75 |  4 (0)| 00:00:01 | 
|* 4 |  INDEX RANGE SCAN   | YFS_SHIPMENT_LINE_H_I4 |  1 |  |  3 (0)| 00:00:01 | 
|* 5 | TABLE ACCESS BY INDEX ROWID| YFS_SHIPMENT_LINE_H |  6 | 450 |  5 (0)| 00:00:01 | 
|* 6 |  INDEX RANGE SCAN   | YFS_SHIPMENT_LINE_H_I6 |  6 |  |  3 (0)| 00:00:01 | 
======================================================================================================== 

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

    4 = access("ORDER_LINE_KEY"='20150113084438815896336') 
    5 = filter(LNNVL("ORDER_LINE_KEY"='20150113084438815896336')) 
    6 = access("ORDER_HEADER_KEY"='20150113083918815889858') 

실행 계획 테이블 YFS_SHIPMENT_LINE_H 두 인덱스 액세스 것을 보여준다 YFS_SHIPMENT_LINE _H_I4 및 YFS_SHIPMENT_LINE_H_I6; 그런 다음 결과가 연결됩니다. 이 계획은 문제가 없으며 쿼리 응답 시간이 좋습니다.

하지만이 전체 쿼리을 실행할 때 내부 쿼리 변경의 액세스 경로는 아래에 주어진 :

PLAN_TABLE_OUTPUT 
======================================================================================================= 
SQL_ID dk1bp8p9g3vzx, child number 0 
===================================== 
SELECT * FROM YFS_SHIPMENT_H WHERE SHIPMENT_KEY IN (SELECT DISTINCT 
SHIPMENT_KEY FROM YFS_SHIPMENT_LINE_H WHERE ORDER_HEADER_KEY = 
'20150113083918815889858' OR (ORDER_LINE_KEY IN (
'20150113084438815896336'))) 

Plan hash value: 3651083773 

======================================================================================================= 
| Id | Operation     | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
======================================================================================================= 
| 0 | SELECT STATEMENT    |      |  |  | 12593 (100)|   | 
| 1 | NESTED LOOPS    |      |  |  |   |   | 
| 2 | NESTED LOOPS    |      |  7 | 6384 | 12593 (1)| 00:02:32 | 
| 3 | SORT UNIQUE    |      |  7 | 525 | 12587 (1)| 00:02:32 | 
|* 4 |  INDEX FAST FULL SCAN  | YFS_SHIPMENT_LINE_H_I2 |  7 | 525 | 12587 (1)| 00:02:32 | 
|* 5 | INDEX UNIQUE SCAN   | YFS_SHIPMENT_H_PK  |  1 |  |  1 (0)| 00:00:01 | 
| 6 | TABLE ACCESS BY INDEX ROWID| YFS_SHIPMENT_H   |  1 | 837 |  2 (0)| 00:00:01 | 
======================================================================================================= 

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

    4 = filter(("ORDER_HEADER_KEY"='20150113083918815889858' OR 
       "ORDER_LINE_KEY"='20150113084438815896336')) 
    5 = access("SHIPMENT_KEY"="SHIPMENT_KEY") 

가 YFS_SHIPMENT_LINE_H 지금 다른 인덱스에 액세스하고 있습니다 (YFS_SHIPMENT_LINE_H_I2). 결과적으로 이것은 매우 좋은 지표는 아니며 쿼리 응답 시간도 겪습니다.

내 질문 : 더 큰 쿼리의 일부로 실행될 때 내부 쿼리 실행 계획이 변경되는 이유는 무엇입니까? 옵티마이 저가 YFS_SHIPMENT_LINE_H에 액세스하는 가장 좋은 방법을 찾았 으면 큰 쿼리의 일부일 때도 동일한 실행 계획을 계속 사용하지 않는 이유는 무엇입니까?

참고 : 올바른 액세스 경로 또는 사용할 인덱스가 무엇인지에 관해선별로 신경 쓰지 않습니다. 따라서 여기에있는 모든 색인을 테이블에 제공하지 마십시오. 및 데이터의 카디널리티. 내 관심사는 다른 쿼리의 일부로 대 별도로 실행될 때의 변경에 관한 것입니다.

감사합니다.

- Parag

답변

0

나는 오라클 최적화 프로그램이 실행 경로를 변경하기로 결정 이유를 모르겠어요.

SELECT s.* 
FROM YFS_SHIPMENT_H s 
WHERE s.SHIPMENT_KEY IN (SELECT sl.SHIPMENT_KEY 
         FROM YFS_SHIPMENT_LINE_H sl 
         WHERE sl.ORDER_HEADER_KEY = '20150113083918815889858' 
         ) OR 
     s.SHIPMENT_KEY IN (SELECT sl.SHIPMENT_KEY 
         FROM YFS_SHIPMENT_LINE_H sl 
         WHERE sl.ORDER_LINE_KEY IN ('20150113084438815896336') 
         ); 

참고 :

  • IN에 대한 하위 쿼리에 SELECT DISTINCT을 할 필요가 없습니다, 나는이 쿼리를 작성하는 더 좋은 방법이라고 생각합니다. 오라클이이를 무시한다고 확신하지만 오버 헤드가 추가 될 수 있습니다.
  • 로직을 두 개의 쿼리로 분리하면 오라클이 쿼리에 인덱스를 사용할 가능성이 높아집니다 (가장 좋은 값은 YFS_SHIPMENT_LINE_H(ORDER_HEADER_KEY, SHIPMENT_KEY)YFS_SHIPMENT_LINE_H(ORDER_LINE_KEY, SHIPMENT_KEY)).
  • (서브 쿼리로 사용되지 않은) 제 쿼리
+0

당신이 옳다는 쿼리가 실제로 다른 방법으로 수행 할 수 있습니다. 이 코드는 내가 변경할 수없는 코드에서 발생합니다. ...... 또한,'in' 절의 일부로'distinct'를 사용할 필요가 없다는 것에 대해 좋은 지적을합니다. –

0

베이스 테이블 where 절의 조건에 따라를 액세스된다. 관련된 두 개의 컬럼에 대한 인덱스는 행 액세스에 사용됩니다.

복잡한 쿼리에서 세미 조인을 수행하고 있습니다. 옵티마이 저는 처음에는 shipment 테이블에서 행을 읽고, shipment_key을 읽고 shipment_line 테이블에서 shipment_key의 인덱스를 사용하여 행을 검색하여 일치하는지 확인했습니다. shipment_line 테이블의 where 절 조건은 이제 필터 조건부 일 뿐이며 테이블에서 검색 할 행을 결정하는 데 사용되지 않습니다.

옵티마이 저가 잘못 생각한 경우 (가능하면 비교적 간단한 쿼리 일 수도 있음) 통계가 최신인지 확인하십시오. 여기서 중요한 것은 각 테이블의 크기, 평균적으로 얼마나 많은 행이 shipment_line에 동일한 shipment_key을 가지며 서브 쿼리의 where 절에있는 조건의 선택도입니다. 외부 쿼리의 경우 하위 쿼리를 전체적으로 계산할 필요가 없습니다 (오라클이 전체적으로 계산하지 않을 가능성이 매우 높음). shipment 테이블의 각 행에 대해 where 절을 충족하는 shipment_line 테이블의 일치하는 행이 발견 되 자마자 shipment_line의 해당 shipment_key에 대한 검색이 중지됩니다.

실제로 할 수있는 한 가지 일은 옵티 마이저가 잘못 생각한 경우 힌트를 사용하면 어떤 일이 발생하는지 확인하는 것입니다. 예를 들어 shipment_line에있는 I2 색인을 사용하지 않도록 최적화 프로그램에 알릴 수 있습니다 (존재하지 않는 것처럼).

+0

당신은 복잡한 쿼리 오라클에서'shipment' 테이블의 행을 먼저 읽은 다음'shipment_key'를 사용하여'shipment line' 테이블을 읽는다 고 언급했습니다. 그러나 실행 계획을 살펴보면 오라클이'yfs_shipment_line_h_i2'에서 빠른 풀 스캔을 사용하여'출하 라인 '을 먼저 읽는 것으로 느껴졌다. 정교하게 주시겠습니까? 감사. –

0

shipment_key의 조인은 옵티마이 저가 가장 선택적 색인 (이 경우 YFS_SHIPMENT_LINE_H_I2 색인)을 사용하도록합니다. 이 쿼리에 대해 스털링이이 인덱스를 만들었지 만 잘못되었습니다. 그것을 삭제 (또는 보이지 않게)하고 쿼리가 올바른 계획을 선택하는 것을보십시오. 인덱스가 Sterling 제품의 일부이므로 인덱스를 삭제하는 것을 주저하는 경우 SQL Plan Management 기준선을 사용하십시오.

YFS_SHIPMENT_LINE_H_I2 SHIPMENT_KEY 1 YFS_SHIPMENT_LINE_H_I2 ORDER_HEADER_KEY 2 YFS_SHIPMENT_LINE_H_I2 ORDER_RELEASE_KEY 3 YFS_SHIPMENT_LINE_H_I2 ORDER_LINE_KEY 4 은 YFS_SHIPMENT_LINE_H_I2 REQUESTED_TAG_NUMBER 5

+0

응답 맨 아래에있는 텍스트 블록이 무엇에 관한 간단한 설명을 추가 할 수 있습니까? –

+0

왜이 색인이 "잘못"이라고 생각하십니까? 또한, 당신은 "조인"에 대해 이야기합니다 - 쿼리가 조인을하지 않는다는 것을 알았습니까? – mathguy

+0

피터, 고마워. Steve/Mathguy, Peter는 질문에 제공된 정보에만 국한되지 않고 전화를 거는 제품에 대한 지식을 기반으로 대답했습니다. 스티브, 피터가 마지막에 추가 한 텍스트 블록은 인덱스 YFS_SHIPMENT_LINE_H_I2에서 사용하는 열입니다. 나는 그것을 (인덱스 이름, 열 이름, 열 위치)로 시작하는 표 형식으로 추측하고 있지만, 어떻게 든 형식을 잃어 버렸습니다. –

관련 문제