【问题标题】:How to index a date column with null values?如何索引具有空值的日期列?
【发布时间】:2011-03-05 08:48:51
【问题描述】:

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

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

我找到的选项

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

我对选项的想法是:

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

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

我读过的一些信息:

Oracle Date Index
When does Oracle index null column values?

编辑

我们的表有 300,000 条记录。每天插入和删除 1,000 到 10,000 条记录。 280,000 条记录的交付日期为空。它是一种拣货缓冲器。

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

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)
)

【问题讨论】:

  • 嗨,1 行有多少字节,有多少行/增长率是多少?
  • 编辑后:如果 300,000 行中有 280,000 行包含空值,并且您想全部选择它们,为什么要索引空值?既然要读整本书,为什么还要按索引来读呢?
  • @Alexander:你的问题很重要。因为它,我分析了我应该首先做的数据。

标签: oracle optimization indexing null


【解决方案1】:

除了 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.

首先我将展示,如果您只是在 a_date 列上创建索引,那么在使用谓词“where a_date is 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

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

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

问候, 抢。

【讨论】:

  • B-tree 索引条目只包含非空值。因此,当您仅在 a_date 列上有一个索引时,该索引包含 9994 个条目。添加一个常量会使所有 10,000 行都出现在索引中,从而适合用于查询。
【解决方案2】:

“我们的表有 300,000 条记录.... 280,000 条记录为空 交付日期。 "

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

如果您拥有 Enterprise Edition 许可证并且有空闲的 CPU,则使用并行查询将减少经过的时间。

【讨论】:

  • 毕竟你绝对正确(就像 Rob 在他的评论中一样)。我投票赞成其他答案。他们可以在其他情况下提供帮助,起初没有关于数据的信息。
  • @HeinzZ - 这是一个非常中肯的观点。数据库都是关于数据的。因此,当涉及到性能调整量和分布(尤其是偏斜分布)时,它们是关键信息。
【解决方案3】:

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

select ...
from mytable
where (datecol between :from and :to
       or datecol is null);

只有在表中相对较少的情况下才值得索引空值 - 否则全表扫描可能是找到它们的最有效方法。假设值得为它们建立索引,您可以创建一个基于函数的索引,如下所示:

create index mytable_fbi on mytable (case when datecol is null then 1 end);

然后将您的查询更改为:

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

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

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');

我确保函数在日期不为空时返回 NULL,以便只有空日期存储在索引中。我还必须将该函数声明为 DETERMINISTIC。 (我将其更改为返回 'Y' 而不是 1,仅仅是因为对我来说“isnull”这个名字暗示它应该这样做;请随意忽略我的偏好!)

【讨论】:

    【解决方案4】:

    避免表查找并像这样创建索引:

    create index i1 on mytable (a_date,id) ;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多