0

继续我的上一篇文章-“将 oracle 迁移到 postgresql 用于编码“UTF8”的无效字节序列:0x00”

我正在尝试从远程 Oracle 表(通过 oracle_fdw 扩展)插入本地 PostgreSQL 表数据。我的 Oracle 表有一个名为 street 的列,它有有效的字符串值,有时还有下一个无效的(在 PostgreSQL 中)字符串:''(空格)。

当我尝试复制列值时,我收到了上面和上一篇文章中提到的错误。我知道在将 Oracle 数据插入 PostgreSQL 之前,我需要更改它。我必须即时执行,所以我尝试在 PostgreSQL 中搜索 oracle decode func。我找到了两个解决方案,我都使用了它们,但我得到了同样的错误:

1.使用选择与案例:

mydb=>select *,(case when v.street=' ' then null END) from customer_prod v;
ERROR:  invalid byte sequence for encoding "UTF8": 0x00
CONTEXT:  converting column "street" for foreign table scan of 
 "customer_prod", row 254148

2.使用 orafce 扩展的解码功能:

mydb=>select decode(street,' ',null) from customer_prod;
ERROR:  invalid byte sequence for encoding "UTF8": 0x00

所以,我仍然收到错误。我该如何解决这个问题?

4

1 回答 1

4

当值从 Oracle 传输到 PostgreSQL 时会发生错误,因此后处理不会阻止错误。

为了演示,让我们创建一个展示问题的 Oracle 表:

CREATE TABLE nulltest(
   id number(5) CONSTRAINT nulltest_pkey PRIMARY KEY,
   val varchar2(10 CHAR)
);

INSERT INTO nulltest VALUES (1, 'schön');
INSERT INTO nulltest VALUES (2, 'bö' || CHR(0) || 'se');
INSERT INTO nulltest VALUES (3, 'egal');

COMMIT;

让我们在 PostgreSQL 中为它创建一个外表:

CREATE FOREIGN TABLE nulltest (
   id integer OPTIONS (key 'true') NOT NULL,
   val varchar(10)
) SERVER oracle
   OPTIONS (table 'NULLTEST');

SELECT * FROM nulltest;

ERROR:  invalid byte sequence for encoding "UTF8": 0x00
CONTEXT:  converting column "val" for foreign table scan of "nulltest", row 2

现在最简单的事情是创建一个过滤掉零字符的外部表:

CREATE FOREIGN TABLE filter_nulltest (
   id integer OPTIONS (key 'true') NOT NULL,
   val varchar(10)
) SERVER oracle
   OPTIONS (table '(SELECT id, replace(val, CHR(0), NULL) FROM nulltest)');

SELECT * FROM filter_nulltest;

┌────┬───────┐
│ id │  val  │
├────┼───────┤
│  1 │ schön │
│  2 │ böse  │
│  3 │ egal  │
└────┴───────┘
(3 rows)

另一个效率较低的选项是创建一个函数来捕获并向您报告错误行,以便您可以在 Oracle 端修复它们:

CREATE OR REPLACE FUNCTION get_nulltest() RETURNS SETOF nulltest
   LANGUAGE plpgsql AS
$$DECLARE
   v_id integer;
   n nulltest;
BEGIN
   FOR v_id IN SELECT id FROM nulltest
   LOOP
      BEGIN
         SELECT nulltest.* INTO n
            FROM nulltest
            WHERE id = v_id;
         RETURN NEXT n;
      EXCEPTION
         WHEN OTHERS THEN
            RAISE NOTICE 'Caught error % for id=%: %', SQLSTATE, v_id, SQLERRM;
      END;
   END LOOP;
END;$$;

SELECT * FROM get_nulltest();

NOTICE:  Caught error 22021 for id=2: invalid byte sequence for encoding "UTF8": 0x00
┌────┬───────┐
│ id │  val  │
├────┼───────┤
│  1 │ schön │
│  3 │ egal  │
└────┴───────┘
(2 rows)
于 2017-07-25T09:15:14.030 回答