Как проиндексировать столбец даты с нулевыми значениями? - PullRequest
13 голосов
/ 18 июня 2010

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

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

Найденные опции

  1. Использование индекса растрового изображенияв столбце даты
  2. Использование индекса по столбцу даты и индекса по полю состояния, значение которого равно 1, когда дата равна нулю
  3. Использование индекса по столбцу даты и других предоставлено, не равно нулюстолбец

Мои мысли о параметрах:

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

Что такоелучшая практика для этого случая?Заранее спасибо

Некоторые сведения, которые я прочитал:

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

Редактировать

В нашей таблице 300 000 записей.От 1000 до 10000 записей вставляются и удаляются каждый день.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)
)

Ответы [ 4 ]

14 голосов
/ 18 июня 2010

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

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

Создайте таблицу с 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 равен нулю":

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 последовательных операций получения и сканирования диапазона индекса.

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

12 голосов
/ 18 июня 2010

"Наша таблица содержит 300 000 записей .... 280 000 записей имеют нулевую дату delivery_at."

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

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

9 голосов
/ 18 июня 2010

Вы имеете в виду, что ваши запросы будут такими?

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, когда дата не равна нулю, так что в индексе хранятся только нулевые даты.Также я должен был объявить функцию как детерминистическую.(Я изменил его, чтобы вернуть «Y» вместо 1 только потому, что для меня имя «isnull» предполагает, что оно должно; не стесняйтесь игнорировать мои предпочтения!)

0 голосов
/ 12 января 2013

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

create index i1 on mytable (a_date,id) ;
...