Opakowanie danych zagranicznych
Zazwyczaj złączenia lub wszelkie tabele pochodne z podzapytań lub CTE nie są dostępne na serwerze obcym i muszą być wykonywane lokalnie. Czyli wszystkie wiersze pozostałe po prostym WHERE
klauzula w twoim przykładzie musi zostać pobrana i przetworzona lokalnie, tak jak zauważyłeś.
Jeśli wszystko inne zawiedzie, możesz wykonać podzapytanie SELECT id FROM lookup_table WHERE x = 5
i połącz wyniki w ciąg zapytania.
Wygodniej możesz to zautomatyzować za pomocą dynamicznego SQL i EXECUTE
w funkcji PL/pgSQL. Na przykład:
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;
Powiązane:
- Nazwa tabeli jako parametr funkcji PostgreSQL
Lub spróbuj tego wyszukiwania na SO.
Możesz też użyć metapolecenia \gexec
w psql. Zobacz:
- Przefiltruj nazwy kolumn z istniejącej tabeli dla instrukcji SQL DDL
Lub to może zadziałać: (Opinia mówi, że nie działa .)
SELECT id,c1,c2,c3
FROM big_table
WHERE c1 = 2
AND id = ANY (ARRAY(SELECT id FROM lookup_table WHERE x = 5));
Testując lokalnie, otrzymuję taki plan zapytań:
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)
Pogrubiony nacisk na moje.
Parametr $0
w planie budzi nadzieję. Wygenerowana tablica może być czymś, co Postgres może przekazać do zdalnego użycia. Nie widzę podobnego planu z żadną z twoich innych prób lub z niektórymi, których sam próbowałem. Czy możesz przetestować za pomocą fdw?
Powiązane pytanie dotyczące postgres_fdw
:
- postgres_fdw:czy można przesłać dane na obcy serwer w celu dołączenia?
Ogólna technika w SQL
To inna historia. Po prostu użyj CTE. Ale nie oczekuję, że to pomoże w 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 zmienione (ulepszone) zachowanie, dzięki czemu CTE mogą być wstawiane jak podzapytania, przy spełnieniu pewnych warunków wstępnych. Ale cytując instrukcję:
Możesz zmienić tę decyzję, określając MATERIALIZED
aby wymusić oddzielne obliczanie zapytania WITH
A więc:
WITH cte AS MATERIALIZED (SELECT id FROM lookup_table WHERE x = 5)
...
Zazwyczaj nie powinno to być konieczne, jeśli serwer bazy danych jest poprawnie skonfigurowany, a statystyki kolumn są aktualne. Ale są przypadki narożne z nierównym rozkładem danych ...