create table test(
id serial primary key,
tagged smallint[]
);
tagged
列上有杜松子酒索引,_int2_ops
运算符类:
CREATE INDEX ix ON test USING GIN(col _int2_ops);
当我运行此查询时:
select * from test
where tagged @> ARRAY[11]
order by id limit 100;
EXPLAIN ANALYZE
显示:
Limit (cost=0.43..19524.39 rows=100 width=36) (actual time=25024.124..25027.263 rows=100 loops=1) -> Index Scan using test_pkey on test (cost=0.43..508404.37 rows=2604 width=36) (actual time=25024.121..25027.251 rows=100 loops=1) Filter: ((tagged)::integer[] @> '{11}'::integer[]) Rows Removed by Filter: 2399999 Planning time: 6.912 ms Execution time: 25027.307 ms
大胆强调我的。为什么tagged
列转换为integer[]
类型?我认为这就是为什么不使用GIN索引并且查询运行缓慢的原因。
我尝试了WHERE tagged @> ARRAY[11]::smallint[]
,但收到了这个错误:
operator is not unique: smallint[] @> smallint[]
如果我这样做但使用tagged int[]
并将索引创建为
CREATE INDEX ix ON test USING GIN(tagged gin__int_ops);
然后上面的查询使用GIN索引:
"-> Bitmap Index Scan on ix (cost=0.00..1575.53 rows=2604 width=0) (actual time=382.840..382.840 rows=2604480 loops=1)" " Index Cond: (tagged @> '{11}'::integer[])"
这比以前快一点,但平均需要10秒 - 仍然太慢。我想尝试smallint[]
代替int[]
,也许会更快......
答案 0 :(得分:1)
最有可能的是,解决方案是对运营商进行架构限定:
SELECT *
FROM test
WHERE tagged OPERATOR(pg_catalog.@>) '{11}'::int2[]
ORDER BY id
LIMIT 100;
这是操作员解析的问题(与类型解析和投射上下文相结合)。
在标准的Postgres中,只有一个候选运算符anyarray @> anyarray
,就是你想要的那个。
如果您没有安装附加模块intarray (我的假设),那么您的设置就可以正常工作,这为integer[] @> integer[]
提供了另一个操作员。
因此,另一种解决方案是使用integer[]
代替并使用gin__int_ops
operator class的GIN索引。或者尝试(默认为intarray)gist__int_ops
索引。要么可能更快,但两者都不允许NULL值
或者,您可以重命名intarray
运算符@>
以消除歧义。 (我不会这样做。随后会出现升级和可移植性问题。)
对于涉及至少一个类型为integer[]
的操作数的表达式,Postgres知道要选择哪个运算符:intarray运算符。但是 索引不适用 ,因为intarray运算符仅对integer
(int4
)而不是int2
进行操作。索引严格限制在运营商身上:
但对于int2[] @> int2[]
,Postgres无法决定最佳运营商。两者似乎同样适用。由于默认运算符在pg_catalog
模式中提供,并且intarray运算符在public
模式中提供(默认情况下 - 或者在您安装扩展的任何位置),您可以通过模式限定来帮助解决难题OPERATOR()
construct的运算符。相关:
您收到的错误消息有点误导。但如果仔细观察,会添加HINT
行,提示(tada!)方向正确:
ERROR: operator is not unique: smallint[] @> smallint[] LINE 1: SELECT NULL::int2[] @> NULL::int2[] ^ HINT: Could not choose a best candidate operator. You might need to add explicit type casts.
您可以使用以下内容调查@>
的现有运营商候选人:
SELECT o.oid, *, oprleft::regtype, oprright::regtype, n.nspname
FROM pg_operator o
JOIN pg_namespace n ON n.oid = o.oprnamespace
WHERE oprname = '@>';
另一种替代解决方案是临时(!)设置不同的search_path,因此只找到所需的运算符。在同一笔交易中:
SET LOCAL search_path = pg_catalog;
SELECT ...
但是你必须对查询中的所有表进行模式限定。
关于演员表:
您可以更改castcontext
的{{1}} - > int2
。但我强烈建议不要这样做。太多可能的副作用: