如何在Oracle 11.2中模拟部分多列(复合)索引?

时间:2018-11-28 20:22:19

标签: oracle indexing plsql oracle11g

大桌子

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;

并创建两个基于函数的索引?

upd: 我需要这样的东西

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 :(得分:1)

您可以在表中添加一个虚拟列,然后将该列包括在索引中:

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列,因为当部门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

具有虚拟列zero_dept *所隐含的department_id = 0谓词。