source

oracle : where 절의 함수가 결과를 필터링한 나머지 모든 where 절 이후에만 호출되도록 하는 방법?

manysource 2023. 10. 2. 15:06

oracle : where 절의 함수가 결과를 필터링한 나머지 모든 where 절 이후에만 호출되도록 하는 방법?

저는 이와 같은 취지로 질문을 씁니다.

select * 
from players 
where player_name like '%K% 
  and player_rank<10 
  and check_if_player_is_eligible(player_name) > 1;

이제 check_if_player_is_elitable() 함수가 무겁기 때문에 쿼리가 검색 결과를 충분히 필터링한 다음 필터링된 결과에 대해서만 이 함수를 실행하기를 원합니다.

기능이 실행되기 전에 최소한의 횟수만큼 실행되도록 모든 필터링이 수행되는지 확인하려면 어떻게 해야 합니까?

다른 모든 WHERE 절을 평가하기 전에 Oracle이 함수를 평가하지 않도록 속일 수 있는 두 가지 방법은 다음과 같습니다.

  1. 로넘 사용

    rownum서브쿼리에서 오라클은 서브쿼리를 "구체화"해야 합니다.를 들어, 예를 들어 Tom 스레드를 묻습니다.

    SELECT *
      FROM (SELECT *
               FROM players
              WHERE player_name LIKE '%K%'
                AND player_rank < 10
                AND ROWNUM >= 1)
     WHERE check_if_player_is_eligible(player_name) > 1
    

    문서 참조 "Nested Subquery의 중첩 해제"는 다음과 같습니다.

    Optimizer는 일부 예외를 제외한 대부분의 하위 쿼리를 제거할 수 있습니다.이러한 예외에는 ROWNUM 의사열, 집합 연산자 중 하나, 중첩 집계 함수 또는 하위 쿼리의 즉시 외부 쿼리 블록이 아닌 쿼리 블록에 대한 상관 참조가 포함된 계층적 하위 쿼리 및 하위 쿼리가 포함됩니다.

  2. CASE 사용하기

    CASE를 사용하면 Oracle이 다른 조건을 TRUE로 평가할 때만 기능을 평가하도록 강제할 수 있습니다.유감스럽게도 다른 절을 사용하여 인덱스를 사용하려면 코드를 복제해야 합니다.

    SELECT *
      FROM players
     WHERE player_name LIKE '%K%'
       AND player_rank < 10
       AND CASE 
             WHEN player_name LIKE '%K%'
              AND player_rank < 10 
                THEN check_if_player_is_eligible(player_name) 
           END > 1
    

이 과정에서 로넘 평가를 수반하지 않고 수행할 수 있는 NO_PUSH_PRED 힌트가 있습니다(어쨌든 좋은 트릭입니다)!

SELECT /*+NO_PUSH_PRED(v)*/*
FROM (
        SELECT *
        FROM players
        WHERE player_name LIKE '%K%'
            AND player_rank < 10
    ) v
 WHERE check_if_player_is_eligible(player_name) > 1

일반적으로 특정한 실행 순서를 강요하지 않기를 원합니다.데이터나 쿼리가 변경되면 힌트와 트릭이 역효과를 낼 수 있습니다.Oracle이 올바른 결정을 내릴 수 있도록 유용한 메타데이터를 제공하는 것이 좋습니다.

이 경우에는 Associate STATRICS를 사용하여 함수에 대한 더 나은 최적화 통계를 제공할 수 있습니다.

예를 들어, 함수가 호출될 때마다 50개의 블록을 읽어야 하기 때문에 함수가 매우 느릴 경우:

associate statistics with functions
check_if_player_is_eligible default cost(1000 /*cpu*/, 50 /*IO*/, 0 /*network*/);

기본적으로 Oracle은 함수가 1/20의 행을 선택한다고 가정합니다.Oracle은 가능한 한 빨리 많은 행을 제거하기를 원하며, 선택도를 변경하면 먼저 실행될 가능성이 낮아집니다.

associate statistics with functions
check_if_player_is_eligible default selectivity 90;

그러나 이것은 몇 가지 다른 문제를 야기합니다.가능한 모든 조건에 대해 선택 항목을 선택해야 합니다. 90%가 항상 정확한 것은 아닙니다.IO 비용은 가져온 블록의 수이지만 CPU 비용은 "사용된 기계 명령"입니다. 정확히 무엇을 의미합니까?

Oracle Data Cartridge Extensible Optimizer를 사용하여 통계를 사용자 정의할 수 있는 보다 향상된 방법이 있습니다.하지만 데이터 카트리지는 아마도 가장 어려운 Oracle 기능 중 하나일 것입니다.

플레이어 여부를 지정하지 않았습니다.player_name이(가) 고유하거나 고유하지 않습니다.그런 다음 데이터베이스가 결과 레코드 당 적어도 한 은 함수를 호출해야 한다고 가정할 수 있습니다.

근데 만약에 선수라면.player_name은(는) 고유하지 않으므로 카운트다운(distinct 플레이어)을 최소화할 수 있습니다.player_name) 횟수입니다.Oracle Magazine에서 (Ask)Tom이 보여주듯이, 스칼라 하위 쿼리 캐시는 이를 위한 효율적인 방법입니다.

스칼라 하위 쿼리 캐시를 사용하려면 함수 호출을 하위 선택으로 래핑해야 합니다.

SELECT players.*
FROM   players,
      (select check_if_player_is_eligible(player.player_name) eligible) subq
WHERE  player_name LIKE '%K%'
  AND  player_rank < 10
  AND  ROWNUM >= 1
  AND  subq.eligible = 1

원래 쿼리를 파생 테이블에 넣은 다음 파생 테이블의 where 절에 추가 술어를 놓습니다.

select * 
from (
   select * 
   from players 
   where player_name like '%K% 
     and player_rank<10 
) derived_tab1
Where  check_if_player_is_eligible(player_name) > 1;

언급URL : https://stackoverflow.com/questions/8428328/oracle-how-to-ensure-that-a-function-in-the-where-clause-will-be-called-only-a