【问题标题】:Making successful use of comparison operators with postgres oracle_fdw使用 postgres oracle_fdw 成功使用比较运算符
【发布时间】:2020-04-12 18:54:29
【问题描述】:

我正在尝试构建一个脚本系统来维护 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 预取限制。它也是我最想在签到之间转移以查看进展情况。

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

【问题讨论】:

  • 为什么要比较数字和字符串?
  • 你用的是什么版本的 Postgres 和 oracle_fdw 的什么版本?
  • PostgresSQL和Oracle中order_number的数据类型是什么?
  • @a_horse_with_no_name 版本应该对此没有影响,但当然最好将它们包含在问题中。
  • @LaurenzAlbe:如果我没记错的话,非常旧的 FDW 版本从来没有下压过任何谓词——但当然你会知道得更好 ;)

标签: oracle postgresql oracle-fdw


【解决方案1】:

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

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

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

【讨论】:

  • 嗯,但是如果你把字符串也发送给Oracle数据库进行比较,那不就意味着只会使用Oracle排序规则吗?
  • 对。但是查询的结果不能因为条件没有被下推而改变。该行为必须遵守 PostgreSQL 语义。
  • 我的预期会有所不同,我认为(至少对我而言)如果在这种情况下可能存在差异是可以接受的。
  • 所以我认为你的推理(在几个方面)是完全公平的。为了澄清,字符串比较在这种情况下确实有效,因为我正在谈论的是 Oracle(也因为我测试了它并且它有效)。问题是如何在 Postgres 中编写语句,以便将其转换为 Oracle 中最终预期的方式。 @LaurenzAlbe 所说的是这是不可能的,因为没有一致的解释,我理解。
  • 但是,如果有替代方案会很好,例如编写直接到 Oracle 查询的方法,或者可能是指定语义偏好的选项标志。例如,如果您设置为 Oracle,则不进行任何翻译,如果您设置为 postgres,则进行任何可以进行的翻译(即当前,默认)。
【解决方案2】:

对此进行跟进以展示我用来解决问题的变通方法。

事实证明,您可以使用文字 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%,这是我绕过障碍的唯一方法。

【讨论】:

    猜你喜欢
    • 2017-03-28
    • 2023-03-26
    • 2020-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-28
    • 1970-01-01
    相关资源
    最近更新 更多