如何在加入推送到国外服务器之前,强制评估子查询。

问题描述 投票:0回答:1

假设我想查询一个大表,里面有几个 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) 预先计算并作为远程过滤器的一部分发送?

sql postgresql sql-execution-plan postgresql-performance foreign-data-wrapper
1个回答
2
投票

外来数据包装器

通常情况下,连接或任何从子查询或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:

SQL中的一般技术

那就另当别论了。就用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服务器配置得当 而且列的统计数据也是最新的,那么这些都是不必要的。但是有一些角落的情况下,数据分布不均 ...

© www.soinside.com 2019 - 2024. All rights reserved.