如何使用空值索引日期列?

Hei*_* Z. 13 oracle indexing optimization null

当某些行具有空值时,我应该如何索引日期列?我们必须在日期范围和具有空日期的行之间选择行.

我们使用Oracle 9.2及更高版本.

我找到的选项

  1. 在日期列上使用位图索引
  2. 使用日期列上的索引和状态字段上的索引,当日期为空时,该值为1
  3. 使用日期列上的索引和其他已授予的非空列

我对选项的看法是:

到1:表必须使用许多不同的值才能使用位图索引
2:我必须仅为此目的添加一个字段,并在我想要将空日期行检索
为3 时更改查询:锁定棘手以添加字段到一个不是真正需要的索引

这种情况的最佳做法是什么?提前致谢

我读过的一些信息:

Oracle Date Index
Oracle什么时候索引空列值?

编辑

我们的表有300,000条记录.每天插入1,000到10,000条记录并删除.280,000条记录的deliver_at日期为null.它是一种挑选缓冲区.

我们的结构(翻译成英文)是:

create table orders
(
  orderid              VARCHAR2(6) not null,
  customerid           VARCHAR2(6) not null,
  compartment          VARCHAR2(8),
  externalstorage      NUMBER(1) default 0 not null,
  created_at           DATE not null,
  last_update          DATE not null,
  latest_delivery      DATE not null,
  delivered_at         DATE,
  delivery_group       VARCHAR2(9),
  fast_order           NUMBER(1) default 0 not null,
  order_type           NUMBER(1) default 0 not null,
  produkt_group        VARCHAR2(30)
)
Run Code Online (Sandbox Code Playgroud)

Rob*_*ijk 14

除了Tony的出色建议之外,还有一个选项可以为您的列编制索引,使您无需调整查询.诀窍是为索引添加一个常量值.

示范:

创建一个包含10,000行的表,其中只有6行包含a_date列的NULL值.

SQL> create table mytable (id,a_date,filler)
  2  as
  3   select level
  4        , case when level < 9995 then date '1999-12-31' + level end
  5        , lpad('*',1000,'*')
  6     from dual
  7  connect by level <= 10000
  8  /

Table created.
Run Code Online (Sandbox Code Playgroud)

首先,我将展示如果您只是在a_date列上创建索引,则在使用谓词"where a_date为null"时不使用索引:

SQL> create index i1 on mytable (a_date)
  2  /

Index created.

SQL> exec dbms_stats.gather_table_stats(user,'mytable',cascade=>true)

PL/SQL procedure successfully completed.

SQL> set autotrace on
SQL> select id
  2       , a_date
  3    from mytable
  4   where a_date is null
  5  /

        ID A_DATE
---------- -------------------
      9995
      9996
      9997
      9998
      9999
     10000

6 rows selected.


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=72 Card=6 Bytes=72)
   1    0   TABLE ACCESS (FULL) OF 'MYTABLE' (Cost=72 Card=6 Bytes=72)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        720  consistent gets
          0  physical reads
          0  redo size
        285  bytes sent via SQL*Net to client
        234  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          6  rows processed
Run Code Online (Sandbox Code Playgroud)

720个一致的获取和全表扫描.

现在更改索引以包含常量1,并重复测试:

SQL> set autotrace off
SQL> drop index i1
  2  /

Index dropped.

SQL> create index i1 on mytable (a_date,1)
  2  /

Index created.

SQL> exec dbms_stats.gather_table_stats(user,'mytable',cascade=>true)

PL/SQL procedure successfully completed.

SQL> set autotrace on
SQL> select id
  2       , a_date
  3    from mytable
  4   where a_date is null
  5  /

        ID A_DATE
---------- -------------------
      9995
      9996
      9997
      9998
      9999
     10000

6 rows selected.


Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=6 Bytes=72)
   1    0   TABLE ACCESS (BY INDEX ROWID) OF 'MYTABLE' (Cost=2 Card=6 Bytes=72)
   2    1     INDEX (RANGE SCAN) OF 'I1' (NON-UNIQUE) (Cost=2 Card=6)


Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
          6  consistent gets
          0  physical reads
          0  redo size
        285  bytes sent via SQL*Net to client
        234  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          6  rows processed
Run Code Online (Sandbox Code Playgroud)

6个一致的获取和索引范围扫描.

问候,Rob.


APC*_*APC 12

"我们的表有300,000条记录...... 280,000条记录的传递日期为空."

换句话说,几乎整个表都满足查询DELIVERED_AT为空的查询.索引完全不适合该搜索.全表扫描是最好的方法.

如果您拥有Enterprise Edition许可证并且备用CPU,则使用并行查询将减少已用时间.


Ton*_*ews 9

你的意思是你的查询是这样的吗?

select ...
from mytable
where (datecol between :from and :to
       or datecol is null);
Run Code Online (Sandbox Code Playgroud)

如果表中的空值相对较少,则只需要对空值进行索引 - 否则全表扫描可能是找到它们的最有效方法.假设值得为它们编制索引,您可以创建一个基于函数的索引,如下所示:

create index mytable_fbi on mytable (case when datecol is null then 1 end);
Run Code Online (Sandbox Code Playgroud)

然后将您的查询更改为:

select ...
from mytable
where (datecol between :from and :to
       or case when datecol is null then 1 end = 1);
Run Code Online (Sandbox Code Playgroud)

您可以将案例包装在函数中以使其更加流畅:

create or replace function isnull (p_date date) return varchar2
DETERMINISTIC
is
begin
    return case when p_date is null then 'Y' end;
end;
/

create index mytable_fbi on mytable (isnull(datecol));

select ...
from mytable
where (datecol between :from and :to
       or isnull(datecol) = 'Y');
Run Code Online (Sandbox Code Playgroud)

我确保当日期不为null时函数返回NULL,以便只在索引中存储空日期.我还必须将该函数声明为DETERMINISTIC.(我把它更改为'Y'而不是1仅仅是因为对我而言"isnull"表示应该这样做;随意忽略我的偏好!)