PostgreSQL 实际上支持数组列上的 GIN 索引。不幸的是,它似乎不适用于NOT ARRAY[...] <@ indexed_col
,而且GIN
索引也不适合频繁更新的表。
演示:
CREATE TABLE arrtable (id integer primary key, array_column integer[]);
INSERT INTO arrtable(1, ARRAY[1,2,3,4]);
CREATE INDEX arrtable_arraycolumn_gin_arr_idx
ON arrtable USING GIN(array_column);
-- Use the following *only* for testing whether Pg can use an index
-- Do not use it in production.
SET enable_seqscan = off;
explain (buffers, analyze) select count(id)
from arrtable
where not (ARRAY[1] <@ arrtable.array_column);
不幸的是,这表明我们不能使用索引。如果您不否定它可以使用的条件,那么您可以搜索并计算包含搜索元素的行(通过删除NOT
)。
您可以使用索引来计算包含目标值的条目,然后从所有条目的计数中减去该结果。由于count
在 PostgreSQL(9.1 和更早版本)中 ing 表中的所有行都非常慢,并且需要顺序扫描,这实际上会比您当前的查询慢。如果在 9.2 上有一个 b-tree 索引,则可以使用仅索引扫描来计算行数id
,在这种情况下,这实际上可能没问题:
SELECT (
SELECT count(id) FROM arrtable
) - (
SELECT count(id) FROM arrtable
WHERE (ARRAY[1] <@ arrtable.array_column)
);
对于 Pg 9.1 及更低版本,它的性能保证比您的原始版本差,因为除了 seqscan 您的原始版本还需要 GIN 索引扫描。我现在已经在 9.2 上对此进行了测试,它似乎确实使用了一个索引来进行计数,所以对于 9.2 来说值得探索。使用一些不那么琐碎的虚拟数据:
drop index arrtable_arraycolumn_gin_arr_idx ;
truncate table arrtable;
insert into arrtable (id, array_column)
select s, ARRAY[1,2,s,s*2,s*3,s/2,s/4] FROM generate_series(1,1000000) s;
CREATE INDEX arrtable_arraycolumn_gin_arr_idx
ON arrtable USING GIN(array_column);
请注意,像这样的 GIN 索引会大大降低更新速度,并且一开始创建速度也很慢。它不适合更新太多的表格——比如你的表格。
更糟糕的是,使用此索引的查询所花费的时间是原始查询的两倍,并且在同一数据集上最多只有一半的时间。对于索引不是很有选择性的情况,例如ARRAY[1]
原始查询的 4s vs 2s,这是最糟糕的。在索引具有高度选择性的情况下(即:没有多少匹配项,例如ARRAY[199]
),它的运行时间约为 1.2 秒,而原始索引的运行时间为 3 秒。这个索引根本不值得这个查询。
这里的教训?有时,正确的答案就是进行顺序扫描。
由于这不会影响您的命中率,因此请按照@debenhur 的建议使用触发器维护物化视图,或者尝试将数组反转为条目没有的参数列表,以便您可以使用 GiST 索引作为@maniek 建议。