PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Comment forcer l'évaluation de la sous-requête avant de rejoindre/pousser vers le serveur étranger

Enveloppe de données étrangères

En règle générale, les jointures ou les tables dérivées des sous-requêtes ou des CTE ne sont pas disponibles sur le serveur étranger et doivent être exécutées localement. C'est-à-dire toutes les lignes restant après le simple WHERE clause dans votre exemple doit être récupérée et traitée localement comme vous l'avez observé.

Si tout le reste échoue, vous pouvez exécuter la sous-requête SELECT id FROM lookup_table WHERE x = 5 et concaténer les résultats dans la chaîne de requête.

Plus facilement, vous pouvez automatiser cela avec SQL dynamique et EXECUTE dans une fonction PL/pgSQL. Comme :

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;

Connexe :

  • Nom de table en tant que paramètre de fonction PostgreSQL

Ou essayez cette recherche sur SO.

Ou vous pouvez utiliser la méta-commande \gexec en psql. Voir :

  • Filtrer les noms de colonne de la table existante pour l'instruction SQL DDL

Ou cela pourrait fonctionner : (Les commentaires indiquent que ne fonctionne pas .)

SELECT id,c1,c2,c3
FROM   big_table
WHERE  c1 = 2
AND    id = ANY (ARRAY(SELECT id FROM lookup_table WHERE x = 5));

En testant localement, j'obtiens un plan de requête comme celui-ci :

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)

J'insiste sur moi.

Le paramètre $0 dans le plan inspire l'espoir. Le tableau généré peut être quelque chose que Postgres peut transmettre pour être utilisé à distance. Je ne vois pas de plan similaire avec aucune de vos autres tentatives ou d'autres que j'ai moi-même essayées. Peux-tu tester avec ton fdw ?

Question connexe concernant postgres_fdw :

  • postgres_fdw :possible de pousser les données vers un serveur étranger pour la jointure ?

Technique générale en SQL

C'est une autre histoire. Utilisez simplement un CTE. Mais je ne m'attends pas à ce que cela aide avec le 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 comportement modifié (amélioré), de sorte que les CTE puissent être alignés comme des sous-requêtes, compte tenu de certaines conditions préalables. Mais, citant le manuel :

Vous pouvez remplacer cette décision en spécifiant MATERIALIZED pour forcer le calcul séparé de la requête WITH

Donc :

WITH cte AS MATERIALIZED (SELECT id FROM lookup_table WHERE x = 5)
...

En règle générale, rien de tout cela ne devrait être nécessaire si votre serveur de base de données est correctement configuré et que les statistiques de colonne sont à jour. Mais il existe des cas extrêmes avec une distribution inégale des données ...