1

以下よりも簡単に言えば、1 つまたは複数のクエリ パラメータ、たとえばx_id(またはレポート/テーブル関数パラメータ) があり、パフォーマンスが重要であり(たとえば、いくつかの主キー インデックスを使用できる)、それ(ユース ケースに応じて) ある場合/report フィルターが適用された、...) の 1 つ

  • null
  • 完全一致(例: 一意の ID)
  • みたいな表現
  • または正規表現

次に、これらすべての可能性が単一のクエリでコード化されている場合、オプティマイザー

  • 実際のパラメーター runtime-value とは無関係に、固有の静的プランを生成します
  • したがって、いくつかのインデックスを使用すると仮定することはできませんx_id、たとえば、完全に一致する可能性があります

これを処理する方法はあります

  1. 一部のPL/SQLコードで、n事前定義済およびユースケースに最適化された問合せ/ビューから選択できるようにしますか?
    • これは、そのような柔軟なパラメーターがあればあるほど、かなり大きくなる可能性があります
  2. または、手動で文字列を構築し、動的にコンパイルしたクエリですか?

基本的に、以下に文書化され実行可能なように、わずかに異なる2つのユースケース/質問があります。

-select * from tf_sel

B -select * from data_union

これは、 SQL ヒントまたはその他のトリックを使用して解決できる可能性があります。

これらのクエリを高速化するために、現在、特定の実装レベル (テーブル関数)"マージされたクエリ" を分離しています。

私が見たように、主な問題は、オプティマイザのSQL計画の静的な性質にあるようです。これは常に同じですが、「クエリ時定数」フィルタパラメータを考慮すると、はるかに効率的になります。

with
    -- Question A: What would be a good strategy to make tf_sel with tf_params nearly as fast as query_use_case_1_eq
    --             which actually provides the same result?
    --
    -- - a complex query should be used in various reports with filters
    -- - we want to keep as much as possible filter functionality on the db side (not the report engine side)
    --   to be able to utilize the fast and efficient db engine and for loosely coupled software design


    complex_query as (  -- just some imaginable complex query with a lot of table/view joins, aggregation/analytical functions etc.
        select 1 as id, 'ab12' as indexed_val, 'asdfasdf' x from dual
        union all select 2, 'ab34', 'a uiop345' from dual
        union all select 3, 'xy34', 'asdf  0u0duaf' from dual
        union all select 4, 'xy55', ' asdja´sf asd' from dual
    )


-- <<< comment the following lines in to test it with the above

--  , query_use_case_1_eq as (  -- quite fast and maybe the 95% use case
--      select * from complex_query where indexed_val = 'ab12'
--  )
--select * from query_use_case_1_eq 

-- >>>

-- ID INDEXED_VAL X
-- -- ----------- --------
--  1 ab12        asdfasdf


-- <<< comment the following lines in to test it with the above

--  , query_use_case_2_all as (  -- significantly slower due to a lot of underlying calculations
--      select * from complex_query
--  )
--select * from query_use_case_2_all

-- >>>

-- ID INDEXED_VAL X
-- -- ----------- -------------
--  1 ab12        asdfasdf
--  2 ab34        a uiop345
--  3 xy34        asdf  0u0duaf
--  4 xy55         asdja´sf asd


-- <<< comment the following lines in to test it with the above

--  , query_use_case_3_like as (
--      select * from complex_query where indexed_val like 'ab%'
--  )
--select * from query_use_case_3_like

-- >>>

-- ID INDEXED_VAL X
-- -- ----------- ---------
--  1 ab12        asdfasdf
--  2 ab34        a uiop345


-- <<< comment the following lines to simulate the table function

    , tf_params as (  -- table function params: imagine we have a table function where these are passed depending on the report
        select  'ab12' p_indexed_val,  'eq' p_filter_type  from dual
    )
    , tf_sel as (  -- table function select: nicely integrating all query possiblities, but beeing veeery slow :-(
        select q.* 
        from 
            tf_params p  -- just here so this example works without the need for the actual function
            join complex_query q on (1=1)
        where
                p_filter_type = 'all'
            or (p_filter_type = 'eq' and indexed_val = p_indexed_val)
            or (p_filter_type = 'like' and indexed_val like p_indexed_val)
            or (p_filter_type = 'regexp' and regexp_like(indexed_val, p_indexed_val))
    )

-- actually we would pass the tf_params above if it were a real table function
select * from tf_sel

-- >>>

-- ID INDEXED_VAL X
-- -- ----------- --------
--  1 ab12        asdfasdf



-- Question B: How can we speed up data_union with dg_filter to be as fast as the data_group1 query which
--             actually provides the same result? 
-- 
-- A very similar approach is considered in other scenarios where we like to join the results of 
-- different queries (>5) returning joinable data and beeing filtered based on the same parameters.

-- <<< comment the following lines to simulate the union problem

--  , data_group1 as (  -- may run quite fast
--      select 'dg1' dg_id, q.* from complex_query q where x < 'a'  -- just an example returning some special rows that should be filtered later on!
--  )
--  
--  , data_group2 as (  -- may run quite fast
--      select 'dg2' dg_id, q.* from complex_query q where instr(x,'p') >= 0  -- just an example returning some special rows that should be filtered later on!
--  )   
--  
--  
--  , dg_filter as (  -- may be set by a report or indirectly by user filters
--      select  'dg1' dg_id  from dual
--  )
--  
--  , data_union as (  -- runs much slower due to another execution plan
--      select * from (
--          select * from data_group1 
--          union all select * from data_group2
--      )
--      where dg_id in (select dg_id from dg_filter)
--  )
--
--select * from data_union

-- >>>

-- DG_ID ID INDEXED_VAL X
-- ----- -- ----------- -------------
-- dg1    4 xy55         asdja´sf asd

これはサンプルコードへのコメントであり、jonearles によって提供された回答です。

実際、あなたの答えは私の(特定のシナリオでは一緒に発生しますが無関係な)ユースケースAとBの混同でした.それでもなお、オプティマイザーには動的なFILTER機能や他の機能があると述べたことは不可欠です.

ユース ケース B (「データ パーティション/グループ ユニオン」)

実際のユース ケース B (サンプル テーブルに基づく) はこのように見えますが、実際のシナリオでパフォーマンスの問題を確認する必要があります。たぶん、すでにいくつかの問題が見られますか?

select * from (
    select 'dg1' data_group, x.* from sample_table x 
        where mod(to_number(some_other_column1), 100000) = 0  -- just some example restriction
            --and indexed_val = '3635'  -- commenting this in and executing this standalone returns:
            ----------------------------------------------------------------------------------------
            --| Id  | Operation                   | Name              | Rows  | Bytes | Cost (%CPU)|
            ----------------------------------------------------------------------------------------
            --|   0 | SELECT STATEMENT            |                   |     1 |    23 |     2   (0)|
            --|   1 |  TABLE ACCESS BY INDEX ROWID| SAMPLE_TABLE      |     1 |    23 |     2   (0)|
            --|   2 |   INDEX RANGE SCAN          | SAMPLE_TABLE_IDX1 |     1 |       |     1   (0)|
            ----------------------------------------------------------------------------------------            
    union all
    select 'dg2', x.* from sample_table x
        where mod(to_number(some_other_column2), 9999) = 0  -- just some example restriction
    union all
    select 'dg3', x.* from sample_table x
        where mod(to_number(some_other_column3), 3635) = 0  -- just some example restriction
)
where data_group in ('dg1') and indexed_val = '35'

-------------------------------------------------------------------------------------------
--| Id  | Operation                      | Name              | Rows  | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------------------
--|   0 | SELECT STATEMENT               |                   |     3 |   639 |     2   (0)|
--|   1 |  VIEW                          |                   |     3 |   639 |     2   (0)|
--|   2 |   UNION-ALL                    |                   |       |       |            |
--|   3 |    TABLE ACCESS BY INDEX ROWID | SAMPLE_TABLE      |     1 |    23 |     2   (0)|
--|   4 |     INDEX RANGE SCAN           | SAMPLE_TABLE_IDX1 |     1 |       |     1   (0)|
--|   5 |    FILTER                      |                   |       |       |            |
--|   6 |     TABLE ACCESS BY INDEX ROWID| SAMPLE_TABLE      |     1 |    23 |     2   (0)|
--|   7 |      INDEX RANGE SCAN          | SAMPLE_TABLE_IDX1 |     1 |       |     1   (0)|
--|   8 |    FILTER                      |                   |       |       |            |
--|   9 |     TABLE ACCESS BY INDEX ROWID| SAMPLE_TABLE      |     1 |    23 |     2   (0)|
--|  10 |      INDEX RANGE SCAN          | SAMPLE_TABLE_IDX1 |     1 |       |     1   (0)|
-------------------------------------------------------------------------------------------

ユース ケース A (列クエリ タイプによるフィルタリング)

あなたのサンプルテーブルに基づいて、これは私がやりたいことにもっと似ています。ご覧のとおり、fast だけを使用したクエリwhere p.ft_id = 'eq' and x.indexed_val = p.valはインデックスの使用状況を示していますが、句にさまざまなフィルター オプションがすべて含まれているwhereと、プランの切り替えで常にフル テーブル スキャンが使用されます : - :p_filter_type/ ( :p_indexed_val_filterSQL は、私が置いた 1 つの場所だけではなく、変更されません。)

with 
    filter_type as (
        select 'all' as id from dual
        union all select 'eq' as id from dual
        union all select 'like' as id from dual
        union all select 'regexp' as id from dual
    )
    , params as (
        select 
            (select * from filter_type where id = :p_filter_type) as ft_id,
            :p_indexed_val_filter as val
        from dual
    )
select * 
from params p
    join sample_table x on (1=1)
    -- the following with the above would show the 'eq' use case with a fast index scan (plan id 14/15)
    --where p.ft_id = 'eq' and x.indexed_val = p.val
    ------------------------------------------------------------------------------------------
    --| Id  | Operation                     | Name              | Rows  | Bytes | Cost (%CPU)|
    ------------------------------------------------------------------------------------------
    --|   0 | SELECT STATEMENT              |                   |     1 |    23 |    12   (0)|
    --|   1 |  VIEW                         |                   |     4 |    20 |     8   (0)|
    --|   2 |   UNION-ALL                   |                   |       |       |            |
    --|   3 |    FILTER                     |                   |       |       |            |
    --|   4 |     FAST DUAL                 |                   |     1 |       |     2   (0)|
    --|   5 |    FILTER                     |                   |       |       |            |
    --|   6 |     FAST DUAL                 |                   |     1 |       |     2   (0)|
    --|   7 |    FILTER                     |                   |       |       |            |
    --|   8 |     FAST DUAL                 |                   |     1 |       |     2   (0)|
    --|   9 |    FILTER                     |                   |       |       |            |
    --|  10 |     FAST DUAL                 |                   |     1 |       |     2   (0)|
    --|  11 |  FILTER                       |                   |       |       |            |
    --|  12 |   NESTED LOOPS                |                   |     1 |    23 |     4   (0)|
    --|  13 |    FAST DUAL                  |                   |     1 |       |     2   (0)|
    --|  14 |    TABLE ACCESS BY INDEX ROWID| SAMPLE_TABLE      |     1 |    23 |     2   (0)|
    --|  15 |     INDEX RANGE SCAN          | SAMPLE_TABLE_IDX1 |     1 |       |     1   (0)|
    --|  16 |   VIEW                        |                   |     4 |    20 |     8   (0)|
    --|  17 |    UNION-ALL                  |                   |       |       |            |
    --|  18 |     FILTER                    |                   |       |       |            |
    --|  19 |      FAST DUAL                |                   |     1 |       |     2   (0)|
    --|  20 |     FILTER                    |                   |       |       |            |
    --|  21 |      FAST DUAL                |                   |     1 |       |     2   (0)|
    --|  22 |     FILTER                    |                   |       |       |            |
    --|  23 |      FAST DUAL                |                   |     1 |       |     2   (0)|
    --|  24 |     FILTER                    |                   |       |       |            |
    --|  25 |      FAST DUAL                |                   |     1 |       |     2   (0)|
    ------------------------------------------------------------------------------------------  
where 
    --mod(to_number(some_other_column1), 3000) = 0 and  -- just some example restriction
    (
            p.ft_id = 'all'
        or
            p.ft_id = 'eq' and x.indexed_val = p.val
        or 
            p.ft_id = 'like' and x.indexed_val like p.val
        or 
            p.ft_id = 'regexp' and regexp_like(x.indexed_val, p.val)
    )
-- with the full flexibility of the filter the plan shows a full table scan (plan id 13) :-(    
--------------------------------------------------------------------------
--| Id  | Operation          | Name         | Rows  | Bytes | Cost (%CPU)|
--------------------------------------------------------------------------
--|   0 | SELECT STATEMENT   |              |  1099 | 25277 |   115   (3)|
--|   1 |  VIEW              |              |     4 |    20 |     8   (0)|
--|   2 |   UNION-ALL        |              |       |       |            |
--|   3 |    FILTER          |              |       |       |            |
--|   4 |     FAST DUAL      |              |     1 |       |     2   (0)|
--|   5 |    FILTER          |              |       |       |            |
--|   6 |     FAST DUAL      |              |     1 |       |     2   (0)|
--|   7 |    FILTER          |              |       |       |            |
--|   8 |     FAST DUAL      |              |     1 |       |     2   (0)|
--|   9 |    FILTER          |              |       |       |            |
--|  10 |     FAST DUAL      |              |     1 |       |     2   (0)|
--|  11 |  NESTED LOOPS      |              |  1099 | 25277 |   115   (3)|
--|  12 |   FAST DUAL        |              |     1 |       |     2   (0)|
--|  13 |   TABLE ACCESS FULL| SAMPLE_TABLE |  1099 | 25277 |   113   (3)|
--|  14 |    VIEW            |              |     4 |    20 |     8   (0)|
--|  15 |     UNION-ALL      |              |       |       |            |
--|  16 |      FILTER        |              |       |       |            |
--|  17 |       FAST DUAL    |              |     1 |       |     2   (0)|
--|  18 |      FILTER        |              |       |       |            |
--|  19 |       FAST DUAL    |              |     1 |       |     2   (0)|
--|  20 |      FILTER        |              |       |       |            |
--|  21 |       FAST DUAL    |              |     1 |       |     2   (0)|
--|  22 |      FILTER        |              |       |       |            |
--|  23 |       FAST DUAL    |              |     1 |       |     2   (0)|
--------------------------------------------------------------------------
4

2 に答える 2

1

いくつかの機能により、オプティマイザは動的計画を作成できます。最も一般的な機能はFILTER操作です。フィルター述語と混同しないでください。FILTER操作により、Oracle は実行時に動的な値に基づいて計画の一部を有効または無効にできます。この機能は通常、バインド変数で機能しますが、他のタイプの動的クエリでは使用できない場合があります。

サンプル スキーマ

create table sample_table
(
    indexed_val        varchar2(100),
    some_other_column1 varchar2(100),
    some_other_column2 varchar2(100),
    some_other_column3 varchar2(100)
);

insert into sample_table
select level, level, level, level
from dual
connect by level <= 100000;

create index sample_table_idx1 on sample_table(indexed_val);

begin
    dbms_stats.gather_table_stats(user, 'sample_table');
end;
/

バインド変数を使用したサンプル クエリ

explain plan for
select * from sample_table where :p_filter_type = 'all'
union all
select * from sample_table where :p_filter_type = 'eq'     and indexed_val = :p_indexed_val
union all
select * from sample_table where :p_filter_type = 'like'   and indexed_val like :p_indexed_val
union all
select * from sample_table where :p_filter_type = 'regexp' and regexp_like(indexed_val, :p_indexed_val);

select * from table(dbms_xplan.display(format => '-cost -bytes -rows'));

サンプルプラン

これは、入力に応じて使用されるプランが大きく異なることを示しています。シングル=は を使用しINDEX RANGE SCAN、述語は を使用しませんTABLE ACCESS FULL。正規表現にはインデックスを作成する方法がないため、正規表現もフル テーブル スキャンを使用します。式の正確なタイプにもよりますが、関数ベースの索引またはOracle Text索引を使用して有用な索引付けを有効にできる場合があります。

Plan hash value: 100704550

------------------------------------------------------------------------------
| Id  | Operation                             | Name              | Time     |
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                      |                   | 00:00:01 |
|   1 |  UNION-ALL                            |                   |          |
|*  2 |   FILTER                              |                   |          |
|   3 |    TABLE ACCESS FULL                  | SAMPLE_TABLE      | 00:00:01 |
|*  4 |   FILTER                              |                   |          |
|   5 |    TABLE ACCESS BY INDEX ROWID BATCHED| SAMPLE_TABLE      | 00:00:01 |
|*  6 |     INDEX RANGE SCAN                  | SAMPLE_TABLE_IDX1 | 00:00:01 |
|*  7 |   FILTER                              |                   |          |
|   8 |    TABLE ACCESS BY INDEX ROWID BATCHED| SAMPLE_TABLE      | 00:00:01 |
|*  9 |     INDEX RANGE SCAN                  | SAMPLE_TABLE_IDX1 | 00:00:01 |
|* 10 |   FILTER                              |                   |          |
|* 11 |    TABLE ACCESS FULL                  | SAMPLE_TABLE      | 00:00:01 |
------------------------------------------------------------------------------

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

   2 - filter(:P_FILTER_TYPE='all')
   4 - filter(:P_FILTER_TYPE='eq')
   6 - access("INDEXED_VAL"=:P_INDEXED_VAL)
   7 - filter(:P_FILTER_TYPE='like')
   9 - access("INDEXED_VAL" LIKE :P_INDEXED_VAL)
       filter("INDEXED_VAL" LIKE :P_INDEXED_VAL)
  10 - filter(:P_FILTER_TYPE='regexp')
  11 - filter( REGEXP_LIKE ("INDEXED_VAL",:P_INDEXED_VAL))
于 2014-03-14T04:44:45.153 に答える
0

(状況A の詳細)だけでなく、 B)にもこのように適用できます ...)

私は現在、いくつかのハイブリッドアプローチ(私の質問の1.と2.のポイントの組み合わせ)を使用していますが、実際には非常に気に入っています.内部ルールなど、より大きなクエリで基本的に論理的に分離されたクエリに基づく戦略。FILTER

  1. これをレポートで使用する

    select * 
    from table(my_report_data_func_sql(
      :val1,
      :val1_filter_type,
      :val2
    ))
    
  2. テーブル関数は次のように定義されています

    create or replace function my_report_data_func_sql(
      p_val1              integer   default 1234,
      p_val1_filter_type  varchar2  default 'eq',
      p_val2              varchar2  default null
    ) return varchar2 is
    
      query varchar2(4000) := ' 
        with params as (  -- *: default param
            select
                ''||p_val1||'' p_val1,                            -- eq*
                '''||p_val1_filter_type||''' p_val1_filter_type,  -- [eq, all*, like, regexp]
                '''||p_val2||''' p_val2                           -- null*
            from dual
        )
        select x.*
        from
            params p  -- workaround for standalone-sql-debugging using "with" statement above
            join my_report_data_base_view x on (1=1)
        where 1=1  -- ease of filter expression adding below
        '          
        -- #### FILTER CRITERIAS are appended here ####
    
        -- val1-filter
        ||case p_val1_filter_type
            when 'eq' then '
            and val1 = p_val1
        '   when 'like' then '
            and val1 like p_val1
        '   when 'regexp' then '
            and regexp_like(val1,  p_val1)
        '   else '' end  -- all
    
      ;
    begin
      return query;
    end;
    ;
    
  3. 例によって次のようになります。

    select * 
    from table(my_report_data_func_sql(
      1234,
      'eq',
      'someval2'
    ))
    
    /*
            with params as (  -- *: default param
                select
                    1          p_val1,              -- eq*
                    'eq'       p_val1_filter_type,  -- [eq, all*, like, regexp]
                    'someval2' p_val2               -- null*
                from dual
            )
            select x.*
            from
                params p  -- workaround for standalone-sql-debugging using "with" statement above
                join my_report_data_base_view x on (1=1)
            where 1=1  -- ease of filter expression adding below
                and val1 = p_val1
    */
    
于 2014-04-04T07:08:39.730 に答える