【问题标题】:Looking in array with a big set of input values查看具有大量输入值的数组
【发布时间】:2015-01-19 13:28:58
【问题描述】:

我有一个看起来像这样的表格(每个表格中的行数示例以获得配给的种类):

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

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

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

还有第二张桌子:

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;

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

可以做些什么来改进查询?

更新:

  • 数组重叠和索引的建议答案在我的设置中效率大大降低。我说不出为什么。
  • 以下版本似乎是最快的(再次说明一下,为什么 - 也许是因为我在 nodeconfigids 中的值通常很少?):

_

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

【问题讨论】:

  • 为了优化性能,我们一般需要更具体的数据。考虑tag info for [postgresql-performance]
  • 顺便说一句,您的 IN (VALUES ...) 表达式语法无效。您正在混合行和设置语法。
  • @ErwinBrandstetter:感谢语法评论,现在更正
  • 使用IN时不需要values关键字,只需使用in ('cf1', 'cf2', ...)
  • @a_horse_with_no_name:我读到使用大量值会更好:postgres.cz/wiki/…。一般是假的吗? (我需要替补)

标签: arrays performance postgresql indexing postgresql-performance


【解决方案1】:

性能的关键是数组列上的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 的数组应该可以正常工作,因为默认的 GIN 运算符类支持 overlap operator &&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);

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

替代unnest()

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

取消嵌套长输入数组(或在您的示例中使用 VALUES 表达式)然后 join 到派生表特别有效。 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);

对于短输入数组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);

【讨论】:

  • 旁白:Postgres 9.4 brought a couple of performance improvements for GIN indexes。当前版本应该对你很有吸引力。
  • 9.4 在很多方面都带来了好处......但我被阻止了,必须支持 postgres 用户安装,从 8.4 开始
  • @fanf42:以上所有内容都应该适用于 pg 8.4+,并且比没有 GIN 索引要快得多。只是效率不如当前版本。我添加了一些没有 GIN 索引的替代方案,但 GIN 索引仍然是灵丹妙药。
  • 谢谢,这绝对有帮助。我将建立一个新的测试台并对其进行测试。在所有情况下,原始问题都被视为已回答,因为我拥有测试和优化所需的所有知识
【解决方案2】:

以下内容避免了数组的取消嵌套,可能会更快:

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

它将返回来自expectedreportsnodes 的行,其中数组中的任何值都出现在nodeconfigids 列中。

【讨论】:

    猜你喜欢
    • 2019-05-11
    • 2018-09-14
    • 2018-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-05
    • 2016-10-22
    • 1970-01-01
    相关资源
    最近更新 更多