假设我想查询一个大表,里面有几个 WHERE
过滤器。我使用的是Postgres 11和一个外来表;外来数据封装器(FDW)是 clickhouse_fdw
. 但我也对一般的解决方案感兴趣。
我可以这样做,如下。
SELECT id,c1,c2,c3 from big_table where id=3 and c1=2
我的FDW可以对远程的外来数据源进行过滤 确保上面的查询速度快,不会拉下太多数据。
如果我写的话,上面的工作原理是一样的。
SELECT id,c1,c2,c3 from big_table where id IN (3,4,5) and c1=2
即所有的过滤都被发送到下游。
但是,如果我想做的过滤稍微复杂一些。
SELECT bt.id,bt.c1,bt.c2,bt.c3
from big_table bt
join lookup_table l on bt.id=l.id
where c1=2 and l.x=5
那么查询规划器就会决定对以下数据进行过滤 c1=2
但在本地应用另一个过滤器。
在我的使用案例中,计算哪个 id
的有 l.x=5
然后再将这些结果发送到远程进行过滤,这样会更快,所以我试着写成下面的方式。
SELECT id,c1,c2,c3
from big_table
where c1=2
and id IN (select id from lookup_table where x=5)
然而,查询规划器仍然决定在本地对所有来自于 big_table
符合 c1=2
这是很慢的。
有没有什么办法可以 "强制" (select id from lookup_table where x=5)
预先计算并作为远程过滤器的一部分发送?
通常情况下,连接或任何从子查询或CTE中派生的表在国外服务器上是不可用的,必须在本地执行。也就是说,在简单的查询之后,剩下的所有行都必须在本地执行。WHERE
子句必须像你观察到的那样在本地进行检索和处理。
如果所有其他方法都失败,你可以执行子查询 SELECT id FROM lookup_table WHERE x = 5
并将结果连接到查询字符串中。
更方便的是,你可以通过动态SQL和 EXECUTE
在PLpgSQL函数中。喜欢。
CREATE OR REPLACE FUNCTION my_func(_c1 int, _l_id int)
RETURNS TABLE(id int, c1 int, c2 int, c3 int) AS
$func$
BEGIN
RETURN QUERY EXECUTE
'SELECT id,c1,c2,c3 FROM big_table
WHERE c1 = $1
AND id = ANY ($2)'
USING _c1
, ARRAY(SELECT l.id FROM lookup_table l WHERE l.x = _l_id);
END
$func$ LANGUAGE plpgsql;
相关:
或者尝试 本次搜索在SO.
或者你可以使用元命令 \gexec
在psql中。请看
或 这可能会有用。 (反馈说 行不通.)
SELECT id,c1,c2,c3
FROM big_table
WHERE c1 = 2
AND id = ANY (ARRAY(SELECT id FROM lookup_table WHERE x = 5));
在本地测试,我得到了这样一个查询计划。
Index Scan using big_table_idx on big_table (cost= ...) Index Cond: (id = ANY ($0)) 过滤器。(c1 = 2) InitPlan 1 (返回) $0)-> Seq Scan on lookup_table (cost= ...) Filter: (x = 5)
粗体强调是我的。
参数 $0
在计划中激发了希望。生成的数组可能是Postgres可以传递给远程使用的东西。我没有看到你的其他尝试或我自己尝试的一些类似的计划。你能用你的fdw测试一下吗?
相关问题 postgres_fdw
:
那就另当别论了。就用CTE吧。但我不指望这对FDW有什么帮助。
WITH cte AS (SELECT id FROM lookup_table WHERE x = 5)
SELECT id,c1,c2,c3
FROM big_table b
JOIN cte USING (id)
WHERE b.c1 = 2;
PostgreSQL 12 改变了(改进了)行为,所以CTE可以像子查询一样内联,给定一些前提条件。但是,引用 手册:
您可以通过指定
MATERIALIZED
强制单独计算WITH查询
所以。
WITH cte AS MATERIALIZED (SELECT id FROM lookup_table WHERE x = 5)
...
通常情况下,如果你的DB服务器配置得当 而且列的统计数据也是最新的,那么这些都是不必要的。但是有一些角落的情况下,数据分布不均 ...