1

我正在尝试构建一个脚本系统来维护 Oracle DB 和 Postgres DB 之间的持续关系。在一个特别大的表(50B 行)上,where 子句 postgres 端的效率显然对于同步至关重要。但是,我发现我在 oracle_fdw 中的选项非常有限,因为 postgres WHERE 子句中的内容实际上转移到了 Oracle 端。在这种情况下...比较运算符。

例如,在我执行此操作时,在 postgres 端

explain
SELECT
    order_number
FROM
    orders 
WHERE
    order_number > '12345'

我得到:

Foreign Scan on orders  (cost=10000.00..35675241480.00 rows=3567523148 width=9)
  Filter: ((order_number)::text > '12345'::text)
  Oracle
    query: SELECT /*0aacedb54006ca3542bcd999c312e859*/ r1."ORDER_NUMBER"
    FROM "ORDERS" r1 
(3 rows)

这表明比较没有到达 Oracle,而是在本地进行过滤之前尝试下载全表。没有布埃诺。

理想情况下,我希望 BETWEEN 条件能够向上翻译。

SELECT
    order_number
FROM
    orders 
WHERE
    order_number between '12345' and '67890'

我一直在考虑的唯一选择是使用硬编码的查询创建一个外部表。唯一的问题是该表仍将返回大量行,而且我无法一次获取和增加批次。我的查询意图是一次只传输大约 10,000 个,因为这接近 fdw 预取限制。它也是我最想在签到之间转移以查看进展情况。

如果有一种方法可以将外部表作为具有可变字段的远程查询,那会很方便吗?

4

2 回答 2

0

oracle_fdw不会在字符串数据类型之间下推不等式运算符,因为此类操作的结果在很大程度上取决于使用的字符串排序规则。

无法保证 Oracle 端使用的排序规则(由 Oracle 编写)的行为与 PostgreSQL 中的排序规则相同。如果条件被按下,查询的行为可能会有所不同,这会产生不正确的结果。

如果您比较数字,请使用数字数据类型。这样的比较将被推倒。

于 2019-12-20T20:00:01.377 回答
0

跟进此以显示我用来解决问题的解决方法。

事实证明,您可以使用文字 SQL 方法创建一个外部表“视图”并即时更新 SQL,如下所示:

CREATE FOREIGN TABLE orders_and_items (
        ... (redacted)
)
SERVER "aasdsd" OPTIONS (table $s$(--QUERY DEFINED BY orders_and_items_range() FUNCTION--)$s$);
ALTER FOREIGN TABLE orders_and_items OPTIONS (ADD prefetch '10000', ADD readonly 'TRUE');

CREATE OR REPLACE FUNCTION orders_and_items_range(IN start_id INT, IN incr_amnt INT) RETURNS VOID AS
$$
DECLARE
    sql_exec TEXT;
BEGIN

incr_amnt := least(incr_amnt,1000000); -- this prevents unintended superlarge increments


-- This is the formal place where the query is defined for the orders_and_items table/view.
sql_exec := 
    $exec$
    ALTER FOREIGN TABLE orders_and_items OPTIONS (SET table $s$(
    SELECT
        *
    FROM orders o
    JOIN ordered_items oi USING (client,order_number,status)
    WHERE
        order_number BETWEEN '%1$s' AND '%2$s'
)$s$);
    $exec$
;

sql_exec := replace(sql_exec, chr(10), ' ');
sql_exec := replace(sql_exec, chr(9), ' ');

sql_exec := format(sql_exec, start_id, start_id + incr_amnt);

EXECUTE sql_exec;

END;
$$
LANGUAGE PLPGSQL
VOLATILE
;

这种方法允许我使用强制将 WHERE 标准传递给 Oracle。用法是这样的:

select orders_and_items_range(67160673, 100000);

select * from from orders_and_items;

这将产生相当于:

    SELECT
        *
    FROM orders o
    JOIN ordered_items oi USING (client,order_number,status)
    WHERE
        order_number BETWEEN '67160673' AND '67260673'

好像它是专门在 Oracle 端执行的,否则我将无法做到。

令人遗憾的是非常 hacky - 但工作非常可靠,速度是 99.9%,是我绕过障碍的唯一方法。

于 2019-12-30T17:57:49.150 回答