Как эмулировать частичный многоколонный (композитный) индекс в Oracle 11.2? - PullRequest
0 голосов
/ 28 ноября 2018

Большой стол

record (
    id number primary key,

    city_id number not null,
    organization_id number not null,
    department_id number not null, -- extra context, can be 0

    renew_date date not null -- frequently updated
)

Используются два вида запросов:

1.

WITH cte (city_id, organization_id, -- etc...) AS (
    -- table join routine...
)
SELECT r.*
FROM record r
INNER JOIN cte t ON r.city_id = t.city_id AND r.organization_id = t.organization_id AND r.department_id = 0
WHERE -- some condition on renew_date... whatever

2.

WITH cte (city_id, organization_id, department_id, -- etc...) AS (
    -- same
)
SELECT r.*
FROM record r
INNER JOIN cte t ON r.city_id = t.city_id AND r.organization_id = t.organization_id AND r.department_id = t.department_id
WHERE -- some condition on renew_date or smth else

Тамходят слухи, что частичный многоколонный (композитный) индекс можно эмулировать в Oracle.https://community.oracle.com/ideas/18213

https://blog.jooq.org/2017/01/18/how-to-emulate-partial-indexes-in-oracle/

Нужно ли создавать пару хэш-функций какого-либо вида

CREATE OR REPLACE FUNCTION get_record_index (city_id IN NUMBER, organization_id IN NUMBER, department_id IN NUMBER)
    RETURN NUMBER
    DETERMINISTIC
AS
BEGIN
    IF department_id <> 0 THEN RETURN NULL
    ELSE RETURN no_idea_how_to_compute(city_id, organization_id);
    END IF;
END;

CREATE OR REPLACE FUNCTION get_record_department_index (city_id IN NUMBER, organization_id IN NUMBER, department_id IN NUMBER)
    RETURN NUMBER
    DETERMINISTIC
AS
BEGIN
    IF department_id = 0 THEN RETURN NULL
    ELSE RETURN no_idea_how_to_compute2(city_id, organization_id, department_id);
    END IF;
END;

и создавать индекс на основе двух функций?

обн: мне нужно что-то подобное

CREATE INDEX record_main_index       ON record (rayon_id, organization_id) WHERE department_id = 0;

CREATE INDEX record_department_index ON record (rayon_id, organization_id, department_id) WHERE department_id <> 0;

1 Ответ

0 голосов
/ 29 ноября 2018

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

ALTER TABLE record ADD zero_dept_id AS (CASE department_id WHEN 0 THEN 0 END);

CREATE INDEX record_paritial_idx ON record (city_id, organization_id, zero_dept_id);

Если подумать, вам может понадобиться только индекс из одного столбца ниже, а не индекс из нескольких столбцоввыше.

CREATE INDEX record_paritial_idx ON record (zero_dept_id);

Тогда в вашем коде вместо r.department_id = 0 вы будете использовать r.zero_dept_id = 0 для использования нового индекса.

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

Для многостолбцового индекса, который действительно удаляет все ненулевые записи Department_id, вам может понадобиться больше виртуальных столбцов:

ALTER TABLE record ADD zero_dept_id AS (CASE department_id WHEN 0 THEN 0 END);
ALTER TABLE record ADD zero_dept_city_id AS (CASE department_id WHEN 0 THEN city_id END);
ALTER TABLE record ADD zero_dept_org_id AS (CASE department_id WHEN 0 THEN organization_id END);

CREATE INDEX record_paritial_idx ON record (zero_dept_city_id, zero_dept_org_id, zero_dept_id);

Тогда в вашем коде выполнитесоответствующие замены, чтобы получить это:

r.zero_dept_city_id = t.city_id AND r.zero_dept_org_id = t.organization_id AND r.zero_dept_id = 0

В последнем варианте выше, вы могли бы удалить столбец zero_dept_id, так как два других виртуальных столбца будут иметь значения в индексе только тогда, когда идентификатор отдела равен нулю, в которомесли индекс станет следующим:

CREATE INDEX record_paritial_idx ON record (zero_dept_city_id, zero_dept_org_id);

, а предикат запроса будет:

r.zero_dept_city_id = t.city_id AND r.zero_dept_org_id = t.organization_id

с подразумеваемым предикатом department_id = 0по нулевой отметке * виртуальные столбцы.

...