Envoltório de dados estrangeiros
Normalmente, as junções ou quaisquer tabelas derivadas de subconsultas ou CTEs não estão disponíveis no servidor externo e precisam ser executadas localmente. Ou seja, todas as linhas restantes após o simples
WHERE
cláusula em seu exemplo deve ser recuperada e processada localmente como você observou. Se tudo mais falhar, você pode executar a subconsulta
SELECT id FROM lookup_table WHERE x = 5
e concatenar os resultados na string de consulta. Mais convenientemente, você pode automatizar isso com SQL dinâmico e
EXECUTE
em uma função PL/pgSQL. Como: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;
Relacionado:
- Nome da tabela como parâmetro de função do PostgreSQL
Ou tente esta pesquisa no SO.
Ou você pode usar o meta-comando
\gexec
em psql. Ver:- Filtrar nomes de colunas da tabela existente para instrução SQL DDL
SELECT id,c1,c2,c3
FROM big_table
WHERE c1 = 2
AND id = ANY (ARRAY(SELECT id FROM lookup_table WHERE x = 5));
Testando localmente, recebo um plano de consulta como este:
Index Scan using big_table_idx on big_table (cost= ...) Index Cond: (id = ANY ($0)) Filter: (c1 = 2) InitPlan 1 (returns $0) -> Seq Scan on lookup_table (cost= ...) Filter: (x = 5)
Minha ênfase em negrito.
O parâmetro
$0
no plano inspira esperança. O array gerado pode ser algo que o Postgres pode passar para ser usado remotamente. Não vejo um plano semelhante em nenhuma de suas outras tentativas ou em algumas outras que eu mesmo tentei. Você pode testar com o seu fdw? Pergunta relacionada sobre
postgres_fdw
:- postgres_fdw:é possível enviar dados para um servidor externo para ingressar?
Técnica geral em SQL
Essa é uma história diferente. Basta usar um CTE. Mas não espero que isso ajude com o 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 comportamento alterado (melhorado), para que os CTEs possam ser embutidos como subconsultas, dadas algumas pré-condições. Mas, citando o manual:
Você pode substituir essa decisão especificandoMATERIALIZED
para forçar o cálculo separado da consulta WITH
Então:
WITH cte AS MATERIALIZED (SELECT id FROM lookup_table WHERE x = 5)
...
Normalmente, nada disso deve ser necessário se o servidor de banco de dados estiver configurado corretamente e as estatísticas da coluna estiverem atualizadas. Mas há casos de canto com distribuição de dados desigual ...