我正在编写python脚本来将表从MSSQL数据库同步到Postgresql数据库。原作者倾向于使用超宽表,其中有许多区域连续的NULL
洞。
对于插入速度,我在execute()
之前以下列形式将记录批量序列化为字符串
INSERT INTO A( {col_list} )
SELECT * FROM ( VALUES (row_1), (row_2),...) B( {col_list} )
在行序列化期间,它不可能在python中确定NULL
或None
的数据类型。这使得工作变得复杂。 NULL
列,timestamp
列等中的所有integer
值都需要显式类型转换为适当的类型,或者Pg抱怨它。
目前,我正在检查DB API connection.description
属性并比较列type_code,对于每个列,并根据需要添加类型转换,如::timestamp
。
但是这需要额外的工作感觉很麻烦:驱动程序已经将数据从文本转换为正确的python数据类型,现在我必须使用那些许多None
s重做它。
有没有更好的方法来解决这个问题,优雅和简洁?
你可以将SELECT
条款直接附加到VALUES
,而不是从INSERT
插入,即:
INSERT INTO A ({col_list})
VALUES (row_1), (row_2), ...
当您从查询中插入时,Postgres在尝试推断列类型时会单独检查查询,然后尝试强制它们匹配目标表(仅发现它不能)。
当您直接从VALUES
列表插入时,它在执行类型推断时知道目标表,然后可以假设任何无类型的NULL
与相应的列匹配。
如果您不需要SELECT
,请使用@Nick's answer。
如果需要它(比如使用CTE多次使用输入行),根据用例的详细信息,有一些变通方法。
例如,使用完整行时:
INSERT INTO A -- complete rows
SELECT * FROM (
VALUES ((NULL::A).*), (row_1), (row_2), ...
) B
OFFSET 1;
在这种特殊情况下,{col_list}
是可选噪声,因为我们无论如何都需要提供完整的行。
详细说明:
您可以尝试使用json_populate_record(..)
从数据创建json,然后从json创建rowset。
postgres=# create table js_test (id int4, dat timestamp, val text);
CREATE TABLE
postgres=# insert into js_test
postgres-# select (json_populate_record(null::js_test,
postgres(# json_object(array['id', 'dat', 'val'], array['5', null, 'test']))).*;
INSERT 0 1
postgres=# select * from js_test;
id | dat | val
----+-----+------
5 | | test
你可以使用json_populate_recordset(..)
一次完成多行。你只需传递json数组,即json数组。确保它不是json数组。
所以这没关系:'[{"id":1,"dat":null,"val":6},{"id":3,"val":"tst"}]'::json
这不是:array['{"id":1,"dat":null,"val":6}'::json,'{"id":3,"val":"tst"}'::json]
select *
from json_populate_recordset(null::js_test,
'[{"id":1,"dat":null,"val":6},{"id":3,"val":"tst"}]')