Как индексировать столбец даты с нулевыми значениями?

Как мне индексировать столбец даты, когда некоторые строки имеют нулевые значения? Мы должны выбирать строки между диапазоном дат и строками с нулевыми датами.

Мы используем Oracle 9.2 и выше.

Параметры, которые я нашел

  • Использование растрового индекса в столбце даты
  • Использование столбца index on date и индекса в поле состояния, значение которого равно 1, когда дата равна null
  • Использование столбца index on date и другого предоставленного столбца без нуля

Мои мысли о вариантах:

до 1: таблица должна иметь множество разных значений, чтобы использовать битмап-индекс
до 2: я должен добавить поле только для этой цели и изменить запрос, когда я хочу получить нулевые строки строк до 3: блокировки сложны, чтобы добавить поле к индексу, который действительно не нужен

Какова наилучшая практика для этого случая? Спасибо заранее

Некоторая информация, которую я прочитал:

Индекс даты Oracle
Когда Oracle индексирует значения нулевого столбца?

Изменить

В нашей таблице 300 000 записей. От 1000 до 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 записей имеют нулевое значение Дата доставки."

Иными словами, почти вся таблица удовлетворяет запросу, который ищет, где DELIVERED_AT имеет значение NULL. Индекс совершенно неуместен для этого поиска. Полное сканирование таблицы - лучший подход.

Если у вас есть лицензия Enterprise Edition, и у вас есть запасные части процессоров, использование параллельного запроса уменьшит время, прошедшее через.

Ответ 2

В дополнение к отличным советам Тони есть также возможность индексировать колонку таким образом, что вам не нужно настраивать свои запросы. Хитрость заключается в том, чтобы добавить постоянное значение только к вашему индексу.

Демонстрация:

Создайте таблицу с 10 000 строк, из которых только 6 содержат значение NULL для столбца 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.

Сначала я покажу, что если вы просто создаете индекс в столбце a_date, индекс не будет использоваться, если вы используете предикат "где 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 согласованных результатов и сканирование диапазона индексов.

С уважением, Роб.

Ответ 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);

Вы можете обернуть случай в функцию, чтобы сделать ее slicker:

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" предполагает, что это должно быть, не стесняйтесь игнорировать мои предпочтения!)

Ответ 4

Избегайте поиска в таблице и создайте индекс следующим образом:

create index i1 on mytable (a_date,id) ;