Postgresql 数组函数不使用索引

问题描述 投票:0回答:1

我创建了一个带有 GIN 索引的 postgresql 数组列,并且我正在尝试对该列执行包含查询。使用标准的 postgresql 我可以像这样正常工作:

SELECT d.name 
FROM deck d 
WHERE d.card_names_array @> string_to_array('"Anger#2","Pingle Who Annoys#2"', ',') 
LIMIT 20;

解释分析:

Limit  (cost=1720.01..1724.02 rows=1 width=31) (actual time=7.787..7.787 rows=0 loops=1)
  ->  Bitmap Heap Scan on deck deck0_  (cost=1720.01..1724.02 rows=1 width=31) (actual time=7.787..7.787 rows=0 loops=1)
        Recheck Cond: (card_names_array @> '{"\"Anger#2\"","\"Pingle Who Annoys#2\""}'::text[])
        ->  Bitmap Index Scan on deck_card_names_array_idx  (cost=0.00..1720.01 rows=1 width=0) (actual time=7.785..7.785 rows=0 loops=1)
              Index Cond: (card_names_array @> '{"\"Anger#2\"","\"Pingle Who Annoys#2\""}'::text[])
Planning time: 0.216 ms
Execution time: 7.810 ms

不幸的是(在这种情况下)我使用的是 QueryDSL,我用它读取像

@>
这样的本机数组函数是无法使用的。然而,这个答案说你可以使用
arraycontains
来代替做同样的事情。我成功了,它返回了正确的结果,但它不使用我的索引。

SELECT d.name 
FROM deck d 
WHERE arraycontains(d.card_names_array, string_to_array('"Anger#2","Pingle Who Annoys#2"', ','))=true
LIMIT 20;

解释分析:

Limit  (cost=0.00..18.83 rows=20 width=31) (actual time=1036.151..1036.151 rows=0 loops=1)
  ->  Seq Scan on deck deck0_  (cost=0.00..159065.60 rows=168976 width=31) (actual time=1036.150..1036.150 rows=0 loops=1)
        Filter: arraycontains(card_names_array, '{"\"Anger#2\"","\"Pingle Who Annoys#2\""}'::text[])
        Rows Removed by Filter: 584014
Planning time: 0.204 ms
Execution time: 1036.166 ms

这是我用于创建布尔表达式的 QueryDSL 代码:

predicate.and(Expressions.booleanTemplate(
        "arraycontains({0}, string_to_array({1}, ','))=true",
        deckQ.cardNamesArray,
        filters.cards.joinToString(",") { "${it.cardName}#${it.quantity}" }
))

有什么办法让它使用我的索引吗?或者也许可以使用 QueryDSL 的不同方法来使用本机

@>
函数?

postgresql jpa spring-data-jpa querydsl
1个回答
0
投票

我们有一个非常相似的问题。不幸的是 arraycontains 不使用索引并顺序扫描表。

对我们有用的另一种方法是扩展 eclipselink 为 @> 添加自定义运算符。我们使用 querydsl 来调用自定义运算符。

  public static final int ArrayContainsOperator = 52170;
  public static final String ArrayContainsOperatorStr = "array_contains";
  public static final ExpressionOperator ARRAY_CONTAINS_OPERATOR = new ExpressionOperator() {
    {
      setSelector(ArrayContainsOperator);
      setName(ArrayContainsOperatorStr);
      this.setType(ExpressionOperator.FunctionOperator);
      Vector v = NonSynchronizedVector.newInstance(3);
      v.add("");
      v.add(" @> ARRAY[");
      v.add("]");
      this.printsAs(v);
      this.bePrefix();
      this.setNodeClass(FunctionExpression.class);
    }
  };
  ExpressionOperator.registerOperator(ArrayContainsOperator, ArrayContainsOperatorStr);
  ExpressionOperator.addOperator(ARRAY_CONTAINS_OPERATOR);

添加运算符后,可以从 querydsl 调用它,如下所示 -

Expressions.booleanTemplate("Operator('" + ArrayContainsOperatorStr + "', {0}, {1})", listPath, value);
© www.soinside.com 2019 - 2024. All rights reserved.