Вопрос

Я создал текстовый индекс Oracle, как следующее:

create index my_idx on my_table (text) indextype is ctxsys.context; 

И тогда я могу сделать следующее:

select * from my_table where contains(text, '%blah%') > 0;

Но скажем, у нас есть еще одна колонка в этой таблице, скажем, group_id, и я хотел вместо этого сделать следующий запрос:

select * from my_table where contains(text, '%blah%') > 0 and group_id = 43;

При вышеуказанном индексе Oracle придется искать все элементы, которые содержат 'blah', а затем проверьте все их group_idс

В идеале я бы предпочел искать только элементы с group_id = 43, так что я бы хотел такой индекс, как это:

create index my_idx on my_table (group_id, text) indextype is ctxsys.context; 

Вроде как обычный индекс, поэтому для каждого можно сделать отдельный текстовый поиск group_id.

Есть ли способ сделать что -то подобное в Oracle (я использую 10G, если это важно)?

Изменить (разъяснение)

Рассмотрим таблицу с миллионом рядов и следующие два столбца среди других, A а также B, оба числовые. Допустим, есть 500 различных значений A и 2000 различных значений B, и каждый ряд уникален.

Теперь давайте рассмотрим select ... where A = x and B = y

Индекс на A а также B отдельно, насколько я могу судить B, который вернет 500 различных строк, а затем сделает соединение/сканирование на этих рядах. В любом случае, по крайней мере 500 строк должны быть рассмотрены (кроме того, что база данных повезло, и находить необходимую строку рано.

В то время как индекс на (A,B) гораздо более эффективен, он находит одну строку в одном поиске индекса.

Поместить отдельные индексы на group_id И текст, который я чувствую, оставляет только генератор запросов с двумя вариантами.

(1) Используйте group_id Индекс и сканируйте все полученные ряды для текста.
(2) Используйте текстовый индекс и сканируйте все полученные ряды для group_id.
(3) Используйте оба индекса и сделайте соединение.

В то время как я хочу:

(4) Используйте (group_id, "text") индекс для поиска текстового индекса под конкретным group_id и сканируйте этот текстовый индекс для конкретной строки/рядов, которые мне нужны. Не требуется сканирование, проверка или соединение, так же, как при использовании индекса на (A,B).

Это было полезно?

Решение

Oracle Text

1 - Вы можете повысить производительность, создав индекс контекста с СОРТИРОВАТЬ ПО:

create index my_idx on my_table(text) indextype is ctxsys.context filter by group_id;

В моих тестах filter by Определенно улучшил производительность, но было все еще немного быстрее использовать индекс Btree на Group_id.

2-Индексы CTXCAT используют «суб-индексы» и, похоже, работают аналогично многоцелевым индексу. Кажется, это вариант (4) Вы ищете:

begin
  ctx_ddl.create_index_set('my_table_index_set');
  ctx_ddl.add_index('my_table_index_set', 'group_id');
end;
/

create index my_idx2 on my_table(text) indextype is ctxsys.ctxcat
    parameters('index set my_table_index_set');

select * from my_table where catsearch(text, 'blah', 'group_id = 43') > 0

Это, вероятно, самый быстрый подход. Использование вышеупомянутого запроса против 120 МБ случайного текста, аналогичного вашему сценарию A и B, требуется только 18 последовательных. Но с другой стороны, создание индекса CTXCAT занял почти 11 минут и использовало 1,8 ГБ пространства.

(ПРИМЕЧАНИЕ: Oracle Text, кажется, работает правильно здесь, но я не знаком с текстом, и я не могу Gaurentee, это не неуместное использование этих индексов, как сказал @nulluserexception.)

Многоколонные индексы против индексных соединений

Для ситуации, которую вы описываете в своем редактировании, обычно Не было бы существенной разницы между использованием индекса на (a, b) и соединением отдельных индексов на A и B. Я создал некоторые тесты с данными, аналогичными тем, которые вы описали Для многоцелевого индекса.

Причина этого в том, что Oracle извлекает данные в блоках. Блок обычно составляет 8K, а индексный блок уже отсортирован, поэтому вы, вероятно, можете соответствовать значениям от 500 до 2000 в нескольких блоках. Если вы беспокоитесь о производительности, обычно IO читать и писать блоки - единственное, что имеет значение. Независимо от того, должен ли Oracle объединиться несколько тысяч рядов - это несущественное количество времени процессора.

Однако это не относится к индексам текста Oracle. Вы можете присоединиться к индексу контекста с индексом Btree («растровый карта и»?), Но производительность плохая.

Другие советы

Я бы поместил индекс на group_id И посмотрите, достаточно ли это хорошо. Вы не говорите, сколько строк мы говорим или о каком производительности вам нужно.

Помните, что порядок, в котором обрабатываются предикаты, не обязательно является порядок, в котором вы написали их в запросе. Не пытайтесь перехитрить оптимизатора, если у вас нет реальной причины.

Укороченная версия: Там нет необходимости делать это. Оптимизатор запроса достаточно умный, чтобы решить, как лучше всего выбрать ваши данные. Просто создайте индекс Btree на group_id, т.е.:

CREATE INDEX my_group_idx ON my_table (group_id); 

Длинная версия: Я создал сценарий (testperf.sql) это вставляет 136 рядов фиктивных данных.

DESC my_table;

Name     Null     Type      
-------- -------- --------- 
ID       NOT NULL NUMBER(4) 
GROUP_ID          NUMBER(4) 
TEXT              CLOB      

Есть индекс Btree на group_id. Анкет Чтобы убедиться, что индекс будет фактически использоваться, запустите это как пользователь DBA:

EXEC DBMS_STATS.GATHER_TABLE_STATS('<YOUR USER HERE>', 'MY_TABLE', cascade=>TRUE);

Вот сколько рядов каждый group_id есть и соответствующий процент:

GROUP_ID               COUNT                  PCT                    
---------------------- ---------------------- ---------------------- 
1                      1                      1                      
2                      2                      1                      
3                      4                      3                      
4                      8                      6                      
5                      16                     12                     
6                      32                     24                     
7                      64                     47                     
8                      9                      7         

Обратите внимание, что оптимизатор запросов будет использовать индекс только в том случае, если он думает, что это хорошая идея, то есть вы получаете до определенного процента рядов. Итак, если вы попросите его о плане запроса:

SELECT * FROM my_table WHERE group_id = 1;
SELECT * FROM my_table WHERE group_id = 7;

Вы увидите, что для первого запроса он будет использовать индекс, тогда как для второго запроса он выполнит полное сканирование таблицы, так как есть слишком много строк, чтобы индекс был эффективным, когда group_id = 7.

Теперь рассмотрим другое состояние - WHERE group_id = Y AND text LIKE '%blah%' (Поскольку я не очень знаком с ctxsys.context).

SELECT * FROM my_table WHERE group_id = 1 AND text LIKE '%ipsum%';

Глядя на план запроса, вы увидите, что он будут Используйте индекс на group_id. Анкет Обратите внимание, что порядок ваших условий не важен:

SELECT * FROM my_table WHERE text LIKE '%ipsum%' AND group_id = 1;

Генерирует тот же план запроса. И если вы попытаетесь запустить один и тот же запрос на group_id = 7, вы увидите, что он возвращается к полному сканированию стола:

SELECT * FROM my_table WHERE group_id = 7 AND text LIKE '%ipsum%';

Обратите внимание, что статистика собирается автоматически Oracle каждый день (она планируется работать каждую ночь и по выходным), чтобы постоянно повышать эффективность оптимизатора запросов. Короче говоря, Oracle делает все возможное, чтобы оптимизировать оптимизатор, поэтому вам не нужно.

У меня нет экземпляра Oracle под рукой, и я не использовал полнотекстовую индексацию в Oracle, но у меня, как правило, была хорошая производительность с встроенные взгляды, что может быть альтернативой такому индексу, который вы имели в виду. Является ли следующий синтаксис законным, когда содержит() вовлечен?

Этот встроенный вид дает вам значения PK строк в группе 43:

             (
             select T.pkcol
             from T
             where group = 43
             )

Если у группы есть нормальный индекс и не имеет низкой кардинальности, извлечение этого набора должен быть быстрым. Тогда вы снова присоединитесь к этому набору с T:

           select * from T
           inner join
            (
             select T.pkcol
             from T
             where group = 43
             ) as MyGroup

           on T.pkcol = MyGroup.pkcol
           where contains(text, '%blah%') > 0

Надеемся, что оптимизатор сможет использовать индекс PK для оптимизации соединения, а затем применить содержит ПРЕДУПРЕЖДЕНИЕ только для групп 43 рядов.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top