仅有两条记录时获得顺序扫描是毫无意义的。对于如此小的数据集,索引永远不会比顺序扫描更快。我建立了一个类似于你的小样本表,并填充了一百万行数据,以下查询风格始终产生良好的计划和快速执行:
prepare s4 as
select id from users
join (select * from (values ($1,$2),($3,$4)) as v(branch, cid)) as p
using (branch, cid);
explain analyze execute s4('b11','c11','b1234','c1234');
QUERY PLAN
Nested Loop (cost=0.00..16.65 rows=1 width=4) (actual time=0.199..0.234 rows=2 loops=1)
-> Values Scan on "*VALUES*" (cost=0.00..0.03 rows=2 width=64) (actual time=0.002..0.003 rows=2 loops=1)
-> Index Scan using u_i on users (cost=0.00..8.30 rows=1 width=16) (actual time=0.111..0.112 rows=1 loops=2)
Index Cond: ((users.branch = "*VALUES*".column1) AND (users.cid = "*VALUES*".column2))
Total runtime: 0.425 ms
看起来你真正的问题是如何将动态确定的值对绑定到你的sql。我的PHP非常生疏,阅读在线文档让我想起了我有多么厌恶它,但我认为以下代码将会做到你想要的,构建上述形式的sql,并根据你希望绑定的值的数量动态创建值对占位符的数量。我手头没有php执行环境,所以我甚至没有检查它是否在语法上正确,但你应该能够理解这个想法并解决我示例中的任何微不足道的错误。
$values = array(
'a', 'b',
'c', 'd',
);
$value_placeholders = "";
$sep = "";
for ($i=1; $i <= $count($values); $i+=2) {
$value_placeholders = $value_placeholders . sprintf("($%u,$%u),", $i, $i+1) . $sep
$sep = ",";
}
$sql =
'select id from users ' .
'join (select * from (values ' . $value_placeholders . ') as v(branch, cid)) as p' .
'using (branch, cid)';
$result = pg_query_params($dbconn, $sql, $values);
如果你真的只需要一个准备好的语句(对于一个懒得在真实数据集上尝试查询而只有两条记录的人,我们将完全避免谈论过早优化),我想我有一个答案:
create index u_i2 on users ((branch||cid));
prepare sa as select id from users where branch||cid in (select unnest($1::text[]));
explain analyze execute sa(ARRAY['b1c1','b1234c1234']);
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------
Nested Loop (cost=12.17..645.78 rows=50000 width=4) (actual time=0.169..0.188 rows=2 loops=1)
-> HashAggregate (cost=0.02..0.03 rows=1 width=32) (actual time=0.018..0.019 rows=2 loops=1)
-> Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.010..0.011 rows=2 loops=1)
-> Bitmap Heap Scan on users (cost=12.14..638.25 rows=500 width=16) (actual time=0.082..0.082 rows=1 loops=2)
Recheck Cond: ((users.branch || users.cid) = (unnest($1)))
-> Bitmap Index Scan on u_i2 (cost=0.00..12.02 rows=500 width=0) (actual time=0.078..0.078 rows=1 loops=2)
Index Cond: ((users.branch || users.cid) = (unnest($1)))
Total runtime: 0.275 ms
注意:我找不到一种方法来获取行对的索引访问。但是,如果您在这两个字段的串联上创建一个功能性索引,然后提供这些串联的边界数组,您将获得一个良好的快速嵌套循环索引扫描。
WHERE branch = ANY($1)
,使用{a,c}
,但随后我无法通过绑定来传递多个列。当我尝试EXPLAIN SELECT id FROM users WHERE (branch, cid) = ANY(ARRAY[('a'::text,'b'::text),('c'::text,'d'::text)]);
并且它做了一次顺序扫描时,我放弃了 ANY。 - Max