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

Oracle 如何用空值索引日期列?,oracle,optimization,indexing,null,Oracle,Optimization,Indexing,Null,当某些行具有空值时,我应该如何索引日期列? 我们必须在日期范围和日期为空的行之间选择行 我们使用Oracle 9.2及更高版本 我找到的选择 在日期列上使用位图索引 使用日期列上的索引和状态字段上的索引,当日期为空时,该字段的值为1 使用“日期”列上的索引和其他“已授予非空”列 我对这些选择的看法是: to 1:表格必须有许多不同的值才能使用位图索引 to 2:我必须仅为此目的添加一个字段,并在检索空日期行时更改查询 to 3:在索引中添加一个实际上不需要的字段非常棘手 这种情况下的最佳做法是什

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

我们使用Oracle 9.2及更高版本

我找到的选择

  • 在日期列上使用位图索引
  • 使用日期列上的索引和状态字段上的索引,当日期为空时,该字段的值为1
  • 使用“日期”列上的索引和其他“已授予非空”列
  • 我对这些选择的看法是:

    to 1:表格必须有许多不同的值才能使用位图索引
    to 2:我必须仅为此目的添加一个字段,并在检索空日期行时更改查询
    to 3:在索引中添加一个实际上不需要的字段非常棘手

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

    我读过一些信息:


    编辑 我们的桌子上有30万条记录。每天插入和删除1000到10000条记录。280000条记录在日期具有空值。它是一种拾取缓冲区

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


    您的意思是您的查询将是这样的吗

    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时返回NULL,以便索引中只存储NULL日期。我还必须将函数声明为确定性函数。(我把它改为返回“Y”而不是1,只是因为对我来说“isnull”这个名字意味着它应该返回;请随意忽略我的偏好!)

    除了Tony的建议之外,还有一个选项可以为列编制索引,这样您就不需要调整查询了。诀窍是只在索引中添加一个常量值

    示范:

    创建一个包含10000行的表,其中只有6行包含a_date列的空值

    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.
    
    720个一致的GET和完整的表扫描

    现在更改索引以包括常数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个一致GET和一个索引范围扫描

    问候,, 罗布

    “我们的表有300000条记录。。。。 280000条记录具有空值 于年月日交付。”

    换句话说,几乎整个表都满足一个查询,该查询搜索传递的位置为null。索引对于该搜索是完全不合适的。全表扫描是最好的方法


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

    避免表查找,并创建如下索引:

    create index i1 on mytable (a_date,id) ;
    

    您好,一行有多少字节,有多少行/增长率是多少?编辑后:如果300000行中有280000行包含空值,并且您希望将它们全部选中,为什么要为空值编制索引?如果你打算阅读整本书,为什么你要按索引阅读一本书?@Alexander:你的问题很重要。正因为如此,我分析了数据,这是我应该首先做的。毕竟你绝对正确(就像罗布在评论中说的那样)。我投票赞成其他人的回答。他们可以在其他情况下提供帮助,起初没有关于数据的信息。@HeinzZ-这是一个非常恰当的观点。数据库都是关于数据的。因此,在性能调整方面,卷和分布(尤其是倾斜分布)是关键信息。B树索引项仅包含非空值。因此,当您仅在a_date列上有一个索引时,该索引包含9994个条目。添加常量会使所有10000行显示在索引中,因此适合用于查询。
    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
    
    create index i1 on mytable (a_date,id) ;