在具有大量输入值的数组中查找

Looking in array with a big set of input values

我有一个 table 看起来像(每行的行数示例以获得定量的种类):

预期报告节点(1 000 000 行):

 nodejoinkey   | integer  | not null
 nodeid        | text     | not null
 nodeconfigids | text[]   | 

nodeconfigids数组一般包含1-50个值。

第二个table:

expectedreports(10 000 行):

 pkid       | integer  | not null
 nodejoinkey| integer  | not null
 ...

我想查询所有预期的报告,它们在 nodeexpectedreports 中存在一个给定 nodeConfigId 的条目。 我可能有大量 nodeConfigIds(数千)。

最有效的方法是什么?

目前,我有:

select E.pkid, E.nodejoinkey from expectedreports E 
inner join (
  select NN.nodejoinkey, NN.nodeid, NN.nodeconfigids from (
    select N.nodejoinkey, N.nodeid, unnest(N.nodeconfigids) as nodeconfigids  
    from expectedreportsnodes N
  ) as NN 
  where NN.nodeconfigids) IN( VALUES ('cf1'), ('cf2'), ..., ('cf1000'), ..., ('cfN')  )
  ) as NNN on E.nodejoinkey = NNN.nodejoinkey;

这似乎给出了预期的结果,但需要很长时间才能执行。

如何改进查询?

更新:

_

select E.pkid, E.nodejoinkey from expectedreports E
inner join (
  select NN.nodejoinkey, NN.nodeconfigids
  from (
    select N.nodejoinkey, N.nodeconfigids, 
           generate_subscripts(N.nodeconfigids,1) as v
    from expectedreportsnodes N
  ) as NN
  where NN.nodeconfigids[v] in(values ('cf1'), ('cf2'), ..., ('cf1000'), ..., ('cfN') )
) as NNN
on E.nodejoinkey = NNN.nodejoinkey

以下避免了数组的嵌套并且可能更快:

select E.pkid, E.nodejoinkey 
from expectedreports E 
  join expectedreportsnodes nn on E.nodejoinkey = NNN.nodejoinkey
where nn.nodeconfigids && array['cf1', 'cf2', ..., 'cf1000', ..., 'cfN'];

它将 return 行 expectedreportsnodes 中数组中的任何值出现在 nodeconfigids 列中的行。

性能的关键是数组列上的 GIN index。并与可以使用索引的运算符一起工作。

CREATE INDEX ern_gin_idx ON expectedreportsnodes USING gin (nodeconfigids);

查询:

SELECT e.pkid, nodejoinkey 
FROM   expectedreports e
JOIN   expectedreportsnodes n USING (nodejoinkey)
WHERE  n.nodeconfigids && '{cf1, cf2, ..., cfN}'::text[];

这对于 text 的数组应该工作得很好,因为 overlap operator && is supported by the default GIN operator class. Per documentation:

Name        Indexed Data Type  Indexable Operators
...
_text_ops   text[]             && <@ = @>
...

还要确保在 expectedreports.nodejoinkey:

上有一个普通的 btree 索引
CREATE INDEX expectedreports_nodejoinkey_idx ON expectedreports (nodejoinkey);

使用多列索引进行优化

要进一步优化给定查询,您可以将原本无用的列 nodejoinkey 包含到索引中以允许仅索引扫描。

要包含 integer 列,首先安装附加模块 btree_gin,它提供必要的 GIN 运算符 类。 运行 每个数据库一次:

CREATE EXTENSION btree_gin;

然后:

CREATE INDEX ern_multi_gin_idx ON expectedreportsnodes
USING gin (nodejoinkey, nodeconfigids);

相同的查询。
更详细的相关答案:

  • Multicolumn index on 3 fields with heterogenous data types

  • What's the proper index for querying structures in arrays in Postgres jsonb?

替代unnest()

如果 GIN 索引不是一个选项(或未达到您的预期),您仍然可以优化查询。

取消嵌套长输入数组(或像您的示例中那样使用 VALUES 表达式)然后 join 到派生的 table 特别有效。 IN 构造通常是最慢的选项。

SELECT e.pkid, nodejoinkey
FROM  (
   SELECT DISTINCT n.nodejoinkey 
   FROM  (SELECT nodejoinkey, unnest(nodeconfigids) AS nodeconfigid
          FROM   expectedreportsnodes) n
   JOIN  (VALUES ('cf1'), ('cf2'), ..., ('cfN')) t(nodeconfigid) USING (nodeconfigid)
   ) n
JOIN   expectedreports e USING (nodejoinkey);

Postgres 中的现代形式 9.3+ 带有隐式 JOIN LATERAL:

SELECT e.pkid, nodejoinkey
FROM  (
   SELECT DISTINCT n.nodejoinkey 
   FROM  expectedreportsnodes n
       , unnest(n.nodeconfigids) nodeconfigid
   JOIN  unnest('{cf1, cf2, ..., cfN}'::text[]) t(nodeconfigid) USING (nodeconfigid)
   ) n
JOIN   expectedreports e USING (nodejoinkey);
  • 您的原始查询可能会在结果中产生重复的行。用 DISTINCT.
  • 折叠
  • JOIN LATERAL 的详细信息:
    • Dynamically execute query using the output of another query

对于短输入数组ANY 构造更快:

SELECT e.pkid, nodejoinkey
FROM  (
   SELECT DISTINCT e.nodejoinkey 
   FROM   expectedreportsnodes e
   JOIN   unnest(e.nodeconfigids) u(nodeconfigid) 
          ON u.nodeconfigid = ANY ('{cf1, cf2, ..., cfN}'::text[])
   ) n
JOIN   expectedreports e USING (nodejoinkey);