当某些行具有空值时,如何索引日期列? 我们必须在日期范围和具有空日期的行之间选择行。
我们使用Oracle 9.2及更高版本。
我找到的选项
我对选项的看法是:
到1:表必须有许多不同的值才能使用位图索引
到2:我必须为此目的添加一个字段,并在我想要检索空日期行时更改查询
到3:锁定难以将字段添加到不真正需要的索引
此案例的最佳做法是什么? 提前致谢
我读过的一些信息:
Oracle Date Index
When does Oracle index null column values?
我们的表有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)
)
答案 0 :(得分: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.
首先,我将展示如果您只是在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
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个一致的获取和索引范围扫描。
此致 罗布。
答案 1 :(得分:12)
“我们的桌子有300,000条记录.... 280,000条记录为空 deliver_at日期。 “
换句话说,几乎整个表都满足查询DELIVERED_AT为空的查询。索引完全不适合该搜索。全表扫描是最好的方法。
如果您拥有Enterprise Edition许可证并且备用CPU,则使用并行查询会减少已用时间。
答案 2 :(得分:9)
你的意思是你的查询是这样的吗?
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,这样只有空日期存储在索引中。我还必须将该函数声明为DETERMINISTIC。 (我把它更改为'Y'而不是1,因为对我来说,名字“isnull”表明它应该;我可以随意忽略我的偏好!)
答案 3 :(得分:0)
避免表查找并创建如下索引:
create index i1 on mytable (a_date,id) ;