在具有大量输入值的数组中查找
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;
这似乎给出了预期的结果,但需要很长时间才能执行。
如何改进查询?
更新:
- 建议的包含数组重叠和索引的答案在我的设置中效率极低。我不能说为什么。
- 以下版本似乎是最快的(同样,我不知道为什么 - 可能是因为我通常在 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
以下避免了数组的嵌套并且可能更快:
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);
我有一个 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;
这似乎给出了预期的结果,但需要很长时间才能执行。
如何改进查询?
更新:
- 建议的包含数组重叠和索引的答案在我的设置中效率极低。我不能说为什么。
- 以下版本似乎是最快的(同样,我不知道为什么 - 可能是因为我通常在 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
以下避免了数组的嵌套并且可能更快:
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
:
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);