суббота, 31 января 2009 г.

[Oracle] Построчное хранение данных

Минимальная единица информации в хранилище Oracle называется блок.

Таблицы хранятся в блоках построчно. Чем меньше суммарный объём полей в записях, тем больше записей помещается в одном блоке.

При чтении какой-либо записи Oracle берёт с диска (или из кеша) весь блок, в котором находится эта запись.

При необходимости перебрать, например, все значения одного поля таблицы, надо прочесть все соответствующие блоки. Предположим, что таблица большая и не помещается в кеш. Тогда, при наличии в таблице «посторонних» неиспользуемых в этом запросе полей, Oracle будет считывать большее количество блоков для получения того же результата.

Пример на Oracle 11:
create table a (n number, s varchar2(4000));
create table b (n number);
begin
for i in 1..1000000 loop
insert into a values(i, lpad('a', 4000, 'a'));
insert into b values(i);
end loop;
end;

set timing on;

select sum(n) from a;

SUM(N)
----------
5.0000E+11

Elapsed: 00:02:04.51

select sum(n) from b;

SUM(N)
----------
5.0000E+11

Elapsed: 00:00:00.11

Хранения «по колонкам» в Oracle не предусмотрено. Для лучшего быстродействия следует избегать объединения в одной таблице полей, которые могут быть востребованы по отдельности.

4 комментария:

  1. Анонимный12/17/2010 2:33 PM

    create index i on a(n)

    ОтветитьУдалить
  2. Да, это годится для данного примера, но не меняет дела в принципе. И не поможет в случаях, когда записи выбираются не подряд, а по списку значений другого индексированного поля, или по списку ROWID, который вернул внешний поисковый движок.

    ОтветитьУдалить
  3. Анонимный12/18/2010 2:17 PM

    Есть возможность индексного hash join на одной таблице, не уверен что это сработает с доменным индексом.

    drop table a;
    create table a (n1 number not null, n2 number not null, s varchar2(4000));
    insert into a select level, level, lpad('x',4000,'x') from dual connect by level < 100000;
    commit;
    create index i1 on a(n1);
    create index i2 on a(n2);
    begin
    dbms_stats.gather_table_stats(user, 'A', estimate_percent => null, cascade => true);
    end;
    /
    explain plan for
    select n1, n2 from a;
    select * from table(dbms_xplan.display);

    | 0 | SELECT STATEMENT |
    | 1 | VIEW | index$_join$_001
    |* 2 | HASH JOIN |
    | 3 | INDEX FAST FULL SCAN| I1
    | 4 | INDEX FAST FULL SCAN| I2

    Predicate Information (identified by operation id):
    ---------------------------------------------------

    2 - access(ROWID=ROWID)

    ОтветитьУдалить
  4. Да, это годится для вашего примера (хотя FULLSCAN всей таблицы, если в ней нет лишнего поля "s", всё равно быстрее) но не меняет дела в принципе :) и не будет работать для случаев, когда интересующее нас поле является BLOB-ом или CLOB-ом. Для доменного индекса тоже не сработает, видимо.

    ОтветитьУдалить