Индексирование не улучшает производительность, а иногда ухудшает ее - PullRequest
0 голосов
/ 22 октября 2018

У меня есть стол EMPLOYEE(EMP_ID, EMP_NAME, DESC, SALARY), и я хочу получить всех сотрудников, которые могут выполнять фокусы.Способность творить магию классифицируется как талант и талант, если каждый сотрудник хранится в конце каждой записи DESC каждого сотрудника.

Если у сотрудника есть какой-либо талант, то DESCу этого сотрудника будет 'Talent: __talent_here__'. Например, Джо умеет жонглировать, тогда его описание будет примерно таким: 'Joe Doe, works here since 2011. Talent: juggling'.Я могу предположить, что в каждом описании есть только один экземпляр строкового литерала 'Talent: ', таланты всегда хранятся в конце DESC, и не у всех сотрудников есть таланты.Я также хочу отсортировать сотрудников, извлеченных по их именам, и хочу, чтобы только первые 50 отсортированных значений

Это утверждение SELECT, которое у меня сейчас есть.

SELECT * FROM (
    SELECT E.EMP_NAME FROM EMP E
    WHERE INSTR(E.DESC, 'Talent: ') > 0
        AND INSTR(SUBSTR(E.DESCRIPTION, INSTR(E.DESC, 'Talent: ')), 'magic') > 0
    ORDER BY E.EMP_NAME ASC
) WHERE ROWNUM <= 50
/

Это SELECT утверждение работает отлично.Теперь я хочу создать индекс запроса для улучшения времени выполнения:

CREATE INDEX MAGICIANS ON EMP(
    INSTR(DESCRIPTION, 'Talent: '), 
    INSTR(SUBSTR(DESCRIPTION, INSTR(DESCRIPTION, 'Talent: ')), 'magic'))
/

Но этот индекс, похоже, не работает.В моей таблице около 9000 записей, и до создания индекса время выполнения оператора SELECT составляет 00.50 секунд.После создания индекса он становится около 00,49 секунд.Я экспериментировал с индексом, и иногда время выполнения после создания индекса даже немного хуже, чем было раньше.(от 00.50 до 00.52 секунд)

Кто-нибудь знает, почему это происходит?

Спасибо всем.

Ответы [ 3 ]

0 голосов
/ 22 октября 2018

Индексы не магические (о, хо!).Создание индекса не гарантирует более быстрое время поиска.Чтобы понять, почему ваш индекс не дает более быстрых результатов, вам нужно понять, как работает индекс.

Ваш индекс упорядочен по смещению слова «Талант».У всех сотрудников без таланта смещение равно нулю, у остальных больше.Для них смещение является функцией длины их значения DESCRIPTION.Важно отметить (предположительно), что нет никакой связи между длиной DESCRIPTION и тем, может ли работник творить чудеса.

Следовательно, чтобы найти сотрудников, которые могут творить чудеса, база данных должна посетить все записи индекса, где смещение talent не равно нулю, затем прочитать все записи индекса, где смещение magic не равно нулю, затемпрочитайте записи таблицы для тех записей.Записи индекса для магов будут разбросаны по всему индексу, а эти записи будут разбросаны по всей таблице.Почти наверняка быстрее выполнить полное сканирование таблицы и извлечь записи из таблицы.

Другое дело, что даже если ваш индекс работал для магов, он был бы бесполезен для поиска фокусников.

По сути, это ошибка модели данных: встраивание TALENT в DESCRIPTION разрывы Первая нормальная форма .Почему можно обойти это - с Виртуальные столбцы (или с Материализованными представлениями, как предложил @TimBiegeisen) - правильное решение было бы для модели TALENT в качестве справочной таблицы и для EMPLOYEE ссылки на нее черезвнешний ключ (таким образом, исключается возможность того, что сотрудники будут практиковать «магию» или «маджик»).Такая справочная таблица также позволит вам иметь много талантливых сотрудников: вы можете смоделировать сотрудников, которые были как фокусниками, так и жонглерами, с таблицей пересечений EMPLOYEE_TALENT, чтобы присоединиться к таблицам EMPLOYEE и TALENT.

0 голосов
/ 22 октября 2018

APC это правильно.Модель данных имеет недостатки.Сказав это, вот два решения.

Решение 1 .Создайте функциональный индекс, извлекая фактический талант.Это позволяет искать жонглера и клоуна.У этого подхода есть два недостатка.Чтобы Oracle использовал этот индекс, вам нужно будет повторить выражение в ваших запросах, чтобы точно соответствовало определению индекса.Вторым недостатком является то, что вы не можете легко извлечь фактический талант, если вы снова не повторите выражение в списке выбора.

create index employee_ix_talent 
    on employee(regexp_replace(descr, '^(.*)Talent: (.*)$', '\2'));

select emp_id, emp_name, salary, regexp_replace(descr, '^(.*)Talent: (.*)$', '\2') as talent
  from employee e
 where regexp_replace(descr, '^(.*)Talent: (.*)$', '\2') = 'magic';

Решение 2 (предпочтительно) .Добавьте виртуальный столбец в таблицу и индексируйте этот столбец.Это избавляет от обоих недостатков от первого решения.Теперь вы можете выбрать столбец талантов и выполнить поиск по нему, а главное: ваш код теперь защищен от изменений в выражении.

alter table employee 
  add talent generated always as(
         regexp_replace(descr, '^(.*)Talent: (.*)$', '\2')
      );

create index employee_ix_talent on employee(talent);

select emp_id, emp_name, talent, salary
  from employee e
 where talent = 'magic';
0 голосов
/ 22 октября 2018

Проблема с вашим индексным подходом состоит в том, что индекс B-Tree строится вокруг start строки в столбце, а не вокруг подстроки, скрытой где-то посередине.Чтобы использовать индекс, в идеале должен быть отдельный добросовестный столбец, содержащий талант сотрудника.Один из подходов здесь состоит в том, чтобы создать материализованное представление, содержащее такой столбец талантов, и затем проиндексировать его:

CREATE MATERIALIZED VIEW mv_name
REFRESH ON DEMAND
AS
SELECT e.*,
    CASE WHEN INSTR(DESCRIPTION, 'Talent: ') > 0
         THEN SUBSTR(DESCRIPTION, INSTR(DESCRIPTION, 'Talent: ') + 8)
     ELSE '' END AS Talent
FROM EMPLOYEE e;

CREATE INDEX idx ON mv_name (Talent);

Теперь следующий запрос должен быть достаточно быстрым:

SELECT *
FROM mv_name
WHERE Talent = 'magic';
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...