未使用 smallint[] 列上的 GIN 索引或错误 "operator is not unique"

GIN index on smallint[] column not used or error "operator is not unique"

create table test(
    id serial primary key,
    tagged smallint[]
);

tagged 列上有 gin 索引,使用 _int2_ops 运算符 class:

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 索引和查询 运行s 慢的原因。

我试过 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[],也许那样会更快...

解决方案

最有可能的是,解决方案 是对运算符进行模式限定:

SELECT *
FROM   test
WHERE  tagged <b>OPERATOR(pg_catalog.@>)</b> '{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 模式中提供了默认运算符,而在 public 模式中提供了 intarray 运算符(默认情况下 - 或者您安装扩展的任何地方),您可以通过模式限定帮助解决难题OPERATOR() construct 运算符。相关:

  • Compare arrays for equality, ignoring order of elements

您收到的错误消息有点误导。但是,如果您仔细观察,会添加一条 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 ...

但是您必须对查询中的所有表进行模式限定。

关于演员背景:

  • Generate series of dates - using date type as input

可以 更改 int2castcontext -> int4。但我强烈反对它。太多可能的副作用:

  • Is there any way to cast postgresql 9.3 data type so that it can affect only one side