Oracle绑定变量未正确使用索引的问题

Oracle绑定变量未正确使用索引的问题,oracle,indexing,parameterized-query,bind-variables,oracle11gr1,Oracle,Indexing,Parameterized Query,Bind Variables,Oracle11gr1,在我的场景中,以下查询运行速度很快(对于一个有7000万行的表,运行时间为0.5秒): 而且,它甚至可以使用绑定变量快速运行: var purchase_id number := 1700656396 select * from Purchases where (purchase_id = :purchase_id) 它们运行得很快,因为我在purchase\u id列上有一个索引。(继续读…) 我需要创建一个允许对任意列进行“筛选”的查询。这意味着提供多个输入变量,并对每个变量进行过滤,除非

在我的场景中,以下查询运行速度很快(对于一个有7000万行的表,运行时间为0.5秒):

而且,它甚至可以使用绑定变量快速运行:

var purchase_id number := 1700656396
select * from Purchases
where (purchase_id = :purchase_id)
它们运行得很快,因为我在
purchase\u id
列上有一个索引。(继续读…)

我需要创建一个允许对任意列进行“筛选”的查询。这意味着提供多个输入变量,并对每个变量进行过滤,除非它是
null
。这一开始效果不错

例如,以下查询运行速度也很快(0.5秒):

但是,当我尝试通过绑定变量或存储过程对查询进行参数化时,查询速度会显著减慢(1.5分钟),就好像它忽略了任何索引:

var purchase_id    number   := 1700656396
var purchase_name  varchar2 := NULL
var purchase_price number   := NULL
select * from Purchases
where (:purchase_id    IS NULL OR purchase_id    = :purchase_id)
and   (:purchase_name  IS NULL OR purchase_name  = :purchase_name)
and   (:purchase_price IS NULL OR purchase_price = :purchase_price)
现在,在我的应用程序中,为了获得良好的性能,我不得不在运行时动态构造查询。这意味着我失去了参数化查询的所有优点,并迫使我担心SQL注入


在保持相同逻辑的同时,是否可以避免动态构造查询?

只是一个简单的问题:我猜下面的非参数化查询也将运行1.5分钟

select * from Purchases
where (1700656396 IS NULL OR purchase_id    = 1700656396)
and   ('some-name' IS NULL OR purchase_name  = 'some-name')
and   (12       IS NULL OR purchase_price = 12)
如果是,问题不是绑定变量,而是缺少索引


编辑问题是,Oracle在为参数化查询生成计划时无法决定使用索引

这确实是一个更大的主题,但我认为这是最容易实现且效果良好的方法。诀窍是使用动态SQL,但要实现它,以便始终传递相同数量的参数(需要),并允许Oracle在没有参数值(当前方法中缺少的)时短路。例如:

set serveroutput on
create or replace procedure test_param(p1 in number default null, p2 in varchar2 default null) as
  l_sql varchar2(4000);
  l_cur sys_refcursor;
  l_rec my_table%rowtype;
  l_ctr number := 0;
begin

  l_sql := 'select * from my_table where 1=1';
  if (p1 is not null) then
    l_sql := l_sql || ' and my_num_col = :p1';
  else
    -- short circuit for optimizer (1=1)
    l_sql := l_sql || ' and (1=1 or :p1 is null)';
  end if;

  if (p2 is not null) then
    l_sql := l_sql || ' and name like :p2';
  else
    -- short circuit for optimizer (1=1)
    l_sql := l_sql || ' and (1=1 or :p2 is null)';
  end if;

  -- show what the SQL query will be
  dbms_output.put_line(l_sql);

  -- note always have same param list (using)
  open l_cur for l_sql using p1,p2;

  -- could return this cursor (function), or simply print out first 10 rows here for testing
  loop
    l_ctr := l_ctr + 1;
    fetch l_cur
    into l_rec;
    exit when l_cur%notfound OR l_ctr > 10;

    dbms_output.put_line('Name is: ' || l_rec.name || ', Address is: ' || l_rec.address1);
  end loop;
  close l_cur;
end;
set serveroutput on
-- using 0 param
exec test_param();
-- using 1 param
exec test_param(123456789);
-- using 2 params
exec test_param(123456789, 'ABC%');
要进行测试,只需运行它。例如:

set serveroutput on
create or replace procedure test_param(p1 in number default null, p2 in varchar2 default null) as
  l_sql varchar2(4000);
  l_cur sys_refcursor;
  l_rec my_table%rowtype;
  l_ctr number := 0;
begin

  l_sql := 'select * from my_table where 1=1';
  if (p1 is not null) then
    l_sql := l_sql || ' and my_num_col = :p1';
  else
    -- short circuit for optimizer (1=1)
    l_sql := l_sql || ' and (1=1 or :p1 is null)';
  end if;

  if (p2 is not null) then
    l_sql := l_sql || ' and name like :p2';
  else
    -- short circuit for optimizer (1=1)
    l_sql := l_sql || ' and (1=1 or :p2 is null)';
  end if;

  -- show what the SQL query will be
  dbms_output.put_line(l_sql);

  -- note always have same param list (using)
  open l_cur for l_sql using p1,p2;

  -- could return this cursor (function), or simply print out first 10 rows here for testing
  loop
    l_ctr := l_ctr + 1;
    fetch l_cur
    into l_rec;
    exit when l_cur%notfound OR l_ctr > 10;

    dbms_output.put_line('Name is: ' || l_rec.name || ', Address is: ' || l_rec.address1);
  end loop;
  close l_cur;
end;
set serveroutput on
-- using 0 param
exec test_param();
-- using 1 param
exec test_param(123456789);
-- using 2 params
exec test_param(123456789, 'ABC%');
在我的系统中,使用的表有超过100mm行,数字字段和名称字段上有索引。几乎立即返回。还请注意,如果不需要所有列,您可能不想执行select*,但我有点懒,在这个示例中使用了%rowtype


希望这有助于

对tbone的答案采取不同的方法,我意识到我可以在代码中动态构造查询,并且仍然使用绑定变量(从而获得索引的灵活性,并且仍然100%不受SQL注入的影响)

在我的代码中,我可以执行以下操作:

string sql = "select * from Purchases where 1 = 1";
if(purchase_id != null)    sql += " and (purchase_id = :purchase_id)";
if(purchase_name != null)  sql += " and (purchase_name = :purchase_name)";
if(purchase_price != null) sql += " and (purchase_price = :purchase_price)";

我对此进行了测试,它解决了我的问题。

虽然听起来很奇怪,但在这种特定情况下,两个组合交叉联接可以有所帮助。
请看下面的示例。

样本数据表:

select * from all_tables;
drop table Purchases;
create table Purchases as
select zx.object_id + (lev-1) * 100000 purchase_id, 
          object_name purchase_name,
          round( dbms_random.value( 1, 200 )) purchase_price,
          zx.* 
from all_objects zx
cross join (select level lev from dual connect by level <= 170);

create unique index purchases_id_ix on Purchases( Purchase_id );

exec dbms_stats.gather_table_stats( user, 'Purchases' );

select count(*) from Purchases;

  COUNT(*)
----------
  10316620
var Purchase_id varchar2( 4000 )
var Purchase_name varchar2( 4000 )
var Purchase_price varchar2( 4000 )

begin
  :Purchase_id := '1139';
  :Purchase_name := NULL;
  :Purchase_price := NULL;
end;
    /

explain plan for
select p.* 
from Purchases p
cross join (
  select 1 from dual d
  where :Purchase_id is not null
) part_1
where Purchase_id = to_number( :Purchase_id )
  and ( :Purchase_name is null or Purchase_name = :Purchase_name )
  and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
union all
select p.* 
from Purchases p
cross join (
  select 1 from dual d
  where :Purchase_id is null
) part_2
where 
  ( :Purchase_name is null or Purchase_name = :Purchase_name )
  and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
;
Plan hash value: 460094106

------------------------------------------------------------------------------------------------------
| Id  | Operation                       | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |                    | 28259 |  5546K| 54093   (1)| 00:10:50 |
|   1 |  NESTED LOOPS                   |                    | 28259 |  5546K| 54093   (1)| 00:10:50 |
|   2 |   FAST DUAL                     |                    |     1 |       |     2   (0)| 00:00:01 |
|   3 |   VIEW                          | VW_JF_SET$96C1679A | 28259 |  5546K| 54091   (1)| 00:10:50 |
|   4 |    UNION-ALL                    |                    |       |       |            |          |
|*  5 |     FILTER                      |                    |       |       |            |          |
|*  6 |      TABLE ACCESS BY INDEX ROWID| PURCHASES          |     1 |   132 |     3   (0)| 00:00:01 |
|*  7 |       INDEX UNIQUE SCAN         | PURCHASES_ID_IX    |     1 |       |     2   (0)| 00:00:01 |
|*  8 |     FILTER                      |                    |       |       |            |          |
|*  9 |      TABLE ACCESS FULL          | PURCHASES          | 28258 |  3642K| 54088   (1)| 00:10:50 |
------------------------------------------------------------------------------------------------------

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

   5 - filter(:PURCHASE_ID IS NOT NULL)
   6 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
              (:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))
   7 - access("P"."PURCHASE_ID"=TO_NUMBER(:PURCHASE_ID))
   8 - filter(:PURCHASE_ID IS NULL)
   9 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
              (:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))

27 wierszy zosta│o wybranych.


解释计划:

select * from all_tables;
drop table Purchases;
create table Purchases as
select zx.object_id + (lev-1) * 100000 purchase_id, 
          object_name purchase_name,
          round( dbms_random.value( 1, 200 )) purchase_price,
          zx.* 
from all_objects zx
cross join (select level lev from dual connect by level <= 170);

create unique index purchases_id_ix on Purchases( Purchase_id );

exec dbms_stats.gather_table_stats( user, 'Purchases' );

select count(*) from Purchases;

  COUNT(*)
----------
  10316620
var Purchase_id varchar2( 4000 )
var Purchase_name varchar2( 4000 )
var Purchase_price varchar2( 4000 )

begin
  :Purchase_id := '1139';
  :Purchase_name := NULL;
  :Purchase_price := NULL;
end;
    /

explain plan for
select p.* 
from Purchases p
cross join (
  select 1 from dual d
  where :Purchase_id is not null
) part_1
where Purchase_id = to_number( :Purchase_id )
  and ( :Purchase_name is null or Purchase_name = :Purchase_name )
  and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
union all
select p.* 
from Purchases p
cross join (
  select 1 from dual d
  where :Purchase_id is null
) part_2
where 
  ( :Purchase_name is null or Purchase_name = :Purchase_name )
  and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
;
Plan hash value: 460094106

------------------------------------------------------------------------------------------------------
| Id  | Operation                       | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |                    | 28259 |  5546K| 54093   (1)| 00:10:50 |
|   1 |  NESTED LOOPS                   |                    | 28259 |  5546K| 54093   (1)| 00:10:50 |
|   2 |   FAST DUAL                     |                    |     1 |       |     2   (0)| 00:00:01 |
|   3 |   VIEW                          | VW_JF_SET$96C1679A | 28259 |  5546K| 54091   (1)| 00:10:50 |
|   4 |    UNION-ALL                    |                    |       |       |            |          |
|*  5 |     FILTER                      |                    |       |       |            |          |
|*  6 |      TABLE ACCESS BY INDEX ROWID| PURCHASES          |     1 |   132 |     3   (0)| 00:00:01 |
|*  7 |       INDEX UNIQUE SCAN         | PURCHASES_ID_IX    |     1 |       |     2   (0)| 00:00:01 |
|*  8 |     FILTER                      |                    |       |       |            |          |
|*  9 |      TABLE ACCESS FULL          | PURCHASES          | 28258 |  3642K| 54088   (1)| 00:10:50 |
------------------------------------------------------------------------------------------------------

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

   5 - filter(:PURCHASE_ID IS NOT NULL)
   6 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
              (:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))
   7 - access("P"."PURCHASE_ID"=TO_NUMBER(:PURCHASE_ID))
   8 - filter(:PURCHASE_ID IS NULL)
   9 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
              (:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))

27 wierszy zosta│o wybranych.


测试:采购id为空

SQL> set pagesize 0
SQL> set linesize 200
SQL> set timing on
SQL> set autotrace traceonly
SQL>
SQL> begin
  2    :Purchase_id := '163027';
  3    :Purchase_name := NULL;
  4    :Purchase_price := NULL;
  5  end;
  6  /

Procedura PL/SQL zosta│a zako˝czona pomyťlnie.

Ca│kowity: 00:00:00.00
SQL> select p.*
  2  from Purchases p
  3  cross join (
  4    select 1 from dual d
  5    where :Purchase_id is not null
  6  ) part_1
  7  where Purchase_id = to_number( :Purchase_id )
  8    and ( :Purchase_name is null or Purchase_name = :Purchase_name )
  9    and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
 10  union all
 11  select p.*
 12  from Purchases p
 13  cross join (
 14    select 1 from dual d
 15    where :Purchase_id is null
 16  ) part_2
 17  where
 18    ( :Purchase_name is null or Purchase_name = :Purchase_name )
 19    and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
 20  ;

Ca│kowity: 00:00:00.09

Plan wykonywania
----------------------------------------------------------
Plan hash value: 460094106

------------------------------------------------------------------------------------------------------
| Id  | Operation                       | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |                    | 28259 |  5546K| 54093   (1)| 00:10:50 |
|   1 |  NESTED LOOPS                   |                    | 28259 |  5546K| 54093   (1)| 00:10:50 |
|   2 |   FAST DUAL                     |                    |     1 |       |     2   (0)| 00:00:01 |
|   3 |   VIEW                          | VW_JF_SET$96C1679A | 28259 |  5546K| 54091   (1)| 00:10:50 |
|   4 |    UNION-ALL                    |                    |       |       |            |          |
|*  5 |     FILTER                      |                    |       |       |            |          |
|*  6 |      TABLE ACCESS BY INDEX ROWID| PURCHASES          |     1 |   132 |     3   (0)| 00:00:01 |
|*  7 |       INDEX UNIQUE SCAN         | PURCHASES_ID_IX    |     1 |       |     2   (0)| 00:00:01 |
|*  8 |     FILTER                      |                    |       |       |            |          |
|*  9 |      TABLE ACCESS FULL          | PURCHASES          | 28258 |  3642K| 54088   (1)| 00:10:50 |
------------------------------------------------------------------------------------------------------

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

   5 - filter(:PURCHASE_ID IS NOT NULL)
   6 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
              (:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))
   7 - access("P"."PURCHASE_ID"=TO_NUMBER(:PURCHASE_ID))
   8 - filter(:PURCHASE_ID IS NULL)
   9 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
              (:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))


Statystyki
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          4  consistent gets
          2  physical reads
          0  redo size
       1865  bytes sent via SQL*Net to client
        519  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
SQL> begin
  2    :Purchase_id := NULL;
  3    :Purchase_name := 'DBMS_CUBE_UTIL';
  4    :Purchase_price := NULL;
  5  end;
  6  /

Procedura PL/SQL zosta│a zako˝czona pomyťlnie.

Ca│kowity: 00:00:00.00
SQL> select p.*
  2  from Purchases p
  3  cross join (
  4    select 1 from dual d
  5    where :Purchase_id is not null
  6  ) part_1
  7  where Purchase_id = to_number( :Purchase_id )
  8    and ( :Purchase_name is null or Purchase_name = :Purchase_name )
  9    and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
 10  union all
 11  select p.*
 12  from Purchases p
 13  cross join (
 14    select 1 from dual d
 15    where :Purchase_id is null
 16  ) part_2
 17  where
 18    ( :Purchase_name is null or Purchase_name = :Purchase_name )
 19    and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
 20  ;

510 wierszy zosta│o wybranych.

Ca│kowity: 00:00:11.90

Plan wykonywania
----------------------------------------------------------
Plan hash value: 460094106

------------------------------------------------------------------------------------------------------
| Id  | Operation                       | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |                    | 28259 |  5546K| 54093   (1)| 00:10:50 |
|   1 |  NESTED LOOPS                   |                    | 28259 |  5546K| 54093   (1)| 00:10:50 |
|   2 |   FAST DUAL                     |                    |     1 |       |     2   (0)| 00:00:01 |
|   3 |   VIEW                          | VW_JF_SET$96C1679A | 28259 |  5546K| 54091   (1)| 00:10:50 |
|   4 |    UNION-ALL                    |                    |       |       |            |          |
|*  5 |     FILTER                      |                    |       |       |            |          |
|*  6 |      TABLE ACCESS BY INDEX ROWID| PURCHASES          |     1 |   132 |     3   (0)| 00:00:01 |
|*  7 |       INDEX UNIQUE SCAN         | PURCHASES_ID_IX    |     1 |       |     2   (0)| 00:00:01 |
|*  8 |     FILTER                      |                    |       |       |            |          |
|*  9 |      TABLE ACCESS FULL          | PURCHASES          | 28258 |  3642K| 54088   (1)| 00:10:50 |
------------------------------------------------------------------------------------------------------

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

   5 - filter(:PURCHASE_ID IS NOT NULL)
   6 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
              (:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))
   7 - access("P"."PURCHASE_ID"=TO_NUMBER(:PURCHASE_ID))
   8 - filter(:PURCHASE_ID IS NULL)
   9 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
              (:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))


Statystyki
----------------------------------------------------------
          0  recursive calls
          0  db block gets
     197993  consistent gets
      82655  physical reads
          0  redo size
      16506  bytes sent via SQL*Net to client
        882  bytes received via SQL*Net from client
         35  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        510  rows processed


测试:Purchase\u id=NULL

SQL> set pagesize 0
SQL> set linesize 200
SQL> set timing on
SQL> set autotrace traceonly
SQL>
SQL> begin
  2    :Purchase_id := '163027';
  3    :Purchase_name := NULL;
  4    :Purchase_price := NULL;
  5  end;
  6  /

Procedura PL/SQL zosta│a zako˝czona pomyťlnie.

Ca│kowity: 00:00:00.00
SQL> select p.*
  2  from Purchases p
  3  cross join (
  4    select 1 from dual d
  5    where :Purchase_id is not null
  6  ) part_1
  7  where Purchase_id = to_number( :Purchase_id )
  8    and ( :Purchase_name is null or Purchase_name = :Purchase_name )
  9    and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
 10  union all
 11  select p.*
 12  from Purchases p
 13  cross join (
 14    select 1 from dual d
 15    where :Purchase_id is null
 16  ) part_2
 17  where
 18    ( :Purchase_name is null or Purchase_name = :Purchase_name )
 19    and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
 20  ;

Ca│kowity: 00:00:00.09

Plan wykonywania
----------------------------------------------------------
Plan hash value: 460094106

------------------------------------------------------------------------------------------------------
| Id  | Operation                       | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |                    | 28259 |  5546K| 54093   (1)| 00:10:50 |
|   1 |  NESTED LOOPS                   |                    | 28259 |  5546K| 54093   (1)| 00:10:50 |
|   2 |   FAST DUAL                     |                    |     1 |       |     2   (0)| 00:00:01 |
|   3 |   VIEW                          | VW_JF_SET$96C1679A | 28259 |  5546K| 54091   (1)| 00:10:50 |
|   4 |    UNION-ALL                    |                    |       |       |            |          |
|*  5 |     FILTER                      |                    |       |       |            |          |
|*  6 |      TABLE ACCESS BY INDEX ROWID| PURCHASES          |     1 |   132 |     3   (0)| 00:00:01 |
|*  7 |       INDEX UNIQUE SCAN         | PURCHASES_ID_IX    |     1 |       |     2   (0)| 00:00:01 |
|*  8 |     FILTER                      |                    |       |       |            |          |
|*  9 |      TABLE ACCESS FULL          | PURCHASES          | 28258 |  3642K| 54088   (1)| 00:10:50 |
------------------------------------------------------------------------------------------------------

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

   5 - filter(:PURCHASE_ID IS NOT NULL)
   6 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
              (:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))
   7 - access("P"."PURCHASE_ID"=TO_NUMBER(:PURCHASE_ID))
   8 - filter(:PURCHASE_ID IS NULL)
   9 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
              (:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))


Statystyki
----------------------------------------------------------
          1  recursive calls
          0  db block gets
          4  consistent gets
          2  physical reads
          0  redo size
       1865  bytes sent via SQL*Net to client
        519  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
SQL> begin
  2    :Purchase_id := NULL;
  3    :Purchase_name := 'DBMS_CUBE_UTIL';
  4    :Purchase_price := NULL;
  5  end;
  6  /

Procedura PL/SQL zosta│a zako˝czona pomyťlnie.

Ca│kowity: 00:00:00.00
SQL> select p.*
  2  from Purchases p
  3  cross join (
  4    select 1 from dual d
  5    where :Purchase_id is not null
  6  ) part_1
  7  where Purchase_id = to_number( :Purchase_id )
  8    and ( :Purchase_name is null or Purchase_name = :Purchase_name )
  9    and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
 10  union all
 11  select p.*
 12  from Purchases p
 13  cross join (
 14    select 1 from dual d
 15    where :Purchase_id is null
 16  ) part_2
 17  where
 18    ( :Purchase_name is null or Purchase_name = :Purchase_name )
 19    and ( :Purchase_price is null or purchase_price = to_number( :Purchase_price ) )
 20  ;

510 wierszy zosta│o wybranych.

Ca│kowity: 00:00:11.90

Plan wykonywania
----------------------------------------------------------
Plan hash value: 460094106

------------------------------------------------------------------------------------------------------
| Id  | Operation                       | Name               | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                |                    | 28259 |  5546K| 54093   (1)| 00:10:50 |
|   1 |  NESTED LOOPS                   |                    | 28259 |  5546K| 54093   (1)| 00:10:50 |
|   2 |   FAST DUAL                     |                    |     1 |       |     2   (0)| 00:00:01 |
|   3 |   VIEW                          | VW_JF_SET$96C1679A | 28259 |  5546K| 54091   (1)| 00:10:50 |
|   4 |    UNION-ALL                    |                    |       |       |            |          |
|*  5 |     FILTER                      |                    |       |       |            |          |
|*  6 |      TABLE ACCESS BY INDEX ROWID| PURCHASES          |     1 |   132 |     3   (0)| 00:00:01 |
|*  7 |       INDEX UNIQUE SCAN         | PURCHASES_ID_IX    |     1 |       |     2   (0)| 00:00:01 |
|*  8 |     FILTER                      |                    |       |       |            |          |
|*  9 |      TABLE ACCESS FULL          | PURCHASES          | 28258 |  3642K| 54088   (1)| 00:10:50 |
------------------------------------------------------------------------------------------------------

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

   5 - filter(:PURCHASE_ID IS NOT NULL)
   6 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
              (:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))
   7 - access("P"."PURCHASE_ID"=TO_NUMBER(:PURCHASE_ID))
   8 - filter(:PURCHASE_ID IS NULL)
   9 - filter((:PURCHASE_NAME IS NULL OR "P"."PURCHASE_NAME"=:PURCHASE_NAME) AND
              (:PURCHASE_PRICE IS NULL OR "P"."PURCHASE_PRICE"=TO_NUMBER(:PURCHASE_PRICE)))


Statystyki
----------------------------------------------------------
          0  recursive calls
          0  db block gets
     197993  consistent gets
      82655  physical reads
          0  redo size
      16506  bytes sent via SQL*Net to client
        882  bytes received via SQL*Net from client
         35  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        510  rows processed


要知道实际执行时间,不要看计划,他们说谎,只包含估计(甲骨文认为会是什么)。查看带有“Ca”的行│它的意思是“总执行时间”(我不知道如何在sqlplus中将代码页更改为英语)。再看看“一致的GET”,这是查询读取的大量逻辑一致块。

第一个查询(采购标识为空)


显然它使用了索引,时间是90ms


第二个查询(purchase\u id=null)



此查询进行全表扫描。

您是否尝试过解释计划并查看封面下的内容?另外,您使用的是什么版本的Oracle?解释计划在使用绑定变量()时没有帮助我使用的是Oracle 11.1.0.7.0。我更新了我的标签,将其包括在内。解释计划仍然会告诉您Oracle认为它将要做什么,即使它最终做了一些不同的事情(您可以跟踪)。这仍然是一个很好的起点。您在
purchase\u name
purchase\u price
上有索引吗?在这种情况下,我只在
purchase\u id
上有索引,其他的没有索引,我也不想在其他的索引上有索引。如果我为这些列提供值,我希望查询运行得很慢。这将需要表扫描,因为它为非索引列
purchase\u name
purchase\u price
提供了一个值,也就是说,oracle不可能在没有表扫描的情况下运行该查询。但是,对于我提供的查询,很容易看出
NULL为NULL
的计算结果应始终为true,因此可以跳过这些
WHERE
子句(实际上,Oracle只对非绑定变量查询这样做)。我不认为“问题在于缺少索引”-我真的不想为每一列都编制索引,我只是希望一些输入可以让它比其他列运行得更快。好的,基本上我们在同一个平台上。问题是,据我所知,在计算参数之前,会为参数化查询生成计划。这样就可以重用计划,从而节省在使用不同参数运行同一查询时再次生成计划的工作量。Oracle如何确定是否可以使用“购买时索引”id字段,或者是否需要从参数化查询中进行完整表扫描?@bpgergo-这在11gR2中得到了改进。您所显示的内容不需要进行完整的表扫描,在筛选器中具有非索引列并不排除使用
purchase\u id
索引。由于这是唯一的索引,我希望它的性能与
null
版本(问题中的第三个查询)完全相同。该计划将只是有不同的过滤器,它不会影响访问。