Answers:
PostgreSQL提供了有关如何最佳地初始填充数据库的指南,他们建议使用COPY命令批量加载行。该指南还提供了一些其他有关如何加快处理速度的很好的技巧,例如在加载数据之前删除索引和外键(然后再添加回去)。
有一种使用COPY的替代方法,它是Postgres支持的多行值语法。从文档中:
INSERT INTO films (code, title, did, date_prod, kind) VALUES
('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy');
上面的代码插入了两行,但是您可以任意扩展它,直到达到准备好的语句标记的最大数量(可能是999美元,但我不确定100%)。有时人们不能使用COPY,这对于那些情况是值得的。
一种加快速度的方法是在一个事务中(例如1000)显式执行多次插入或复制。Postgres的默认行为是在每个语句之后提交,因此通过批量提交,可以避免一些开销。正如丹尼尔答案中的指南所述,您可能必须禁用自动提交才能正常工作。还要注意底部的注释,该注释建议将wal_buffers的大小增加到16 MB也可能有帮助。
UNNEST
带有数组的函数可以与多行VALUES语法一起使用。我认为此方法比使用慢,COPY
但对我使用psycopg和python(list
传递给cursor.execute
pg的python ARRAY
)很有用:
INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
VALUES (
UNNEST(ARRAY[1, 2, 3]),
UNNEST(ARRAY[100, 200, 300]),
UNNEST(ARRAY['a', 'b', 'c'])
);
无需VALUES
将subselect与其他存在检查一起使用:
INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
SELECT * FROM (
SELECT UNNEST(ARRAY[1, 2, 3]),
UNNEST(ARRAY[100, 200, 300]),
UNNEST(ARRAY['a', 'b', 'c'])
) AS temptable
WHERE NOT EXISTS (
SELECT 1 FROM tablename tt
WHERE tt.fieldname1=temptable.fieldname1
);
与批量更新相同的语法:
UPDATE tablename
SET fieldname1=temptable.data
FROM (
SELECT UNNEST(ARRAY[1,2]) AS id,
UNNEST(ARRAY['a', 'b']) AS data
) AS temptable
WHERE tablename.id=temptable.id;
您可以使用COPY table TO ... WITH BINARY
“ 比文本和CSV格式更快的速度 ”。仅当您要插入数百万行并且对二进制数据感到满意时,才执行此操作。
我使用本机libpq方法实现了非常快速的Postgresq数据加载器。试试我的包https://www.nuget.org/packages/NpgsqlBulkCopy/
术语“大量数据”与“大量数据”相关,因此很自然地使用原始原始数据,而无需将其转换为SQL。“批量插入”的典型原始数据文件为CSV和JSON格式。
在ETL应用程序和提取过程中,我们需要在插入数据之前对其进行更改。临时表会占用(大量)磁盘空间,但这并不是更快的方法。在PostgreSQL的外商数据封装器(FDW)是最好的选择。
CSV示例。假设tablename (x, y, z)
on SQL和CSV文件类似
fieldname1,fieldname2,fieldname3
etc,etc,etc
... million lines ...
您可以使用经典的SQL COPY
加载(作为原始数据)到tmp_tablename
,它们将过滤后的数据插入到tablename
...中,但是,为了避免占用磁盘,最好的方法是直接通过
INSERT INTO tablename (x, y, z)
SELECT f1(fieldname1), f2(fieldname2), f3(fieldname3) -- the transforms
FROM tmp_tablename_fdw
-- WHERE condictions
;
您需要为FDW准备数据库,而tmp_tablename_fdw
可以使用静态函数来生成它:
CREATE EXTENSION file_fdw;
CREATE SERVER import FOREIGN DATA WRAPPER file_fdw;
CREATE FOREIGN TABLE tmp_tablename_fdw(
...
) SERVER import OPTIONS ( filename '/tmp/pg_io/file.csv', format 'csv');
JSON示例。一组的两个文件,myRawData1.json
并且Ranger_Policies2.json
可以通过摄入:
INSERT INTO tablename (fname, metadata, content)
SELECT fname, meta, j -- do any data transformation here
FROM jsonb_read_files('myRawData%.json')
-- WHERE any_condiction_here
;
函数jsonb_read_files()读取文件夹的所有文件(由掩码定义):
CREATE or replace FUNCTION jsonb_read_files(
p_flike text, p_fpath text DEFAULT '/tmp/pg_io/'
) RETURNS TABLE (fid int, fname text, fmeta jsonb, j jsonb) AS $f$
WITH t AS (
SELECT (row_number() OVER ())::int id,
f as fname,
p_fpath ||'/'|| f as f
FROM pg_ls_dir(p_fpath) t(f)
WHERE f like p_flike
) SELECT id, fname,
to_jsonb( pg_stat_file(f) ) || jsonb_build_object('fpath',p_fpath),
pg_read_file(f)::jsonb
FROM t
$f$ LANGUAGE SQL IMMUTABLE;
最常见的“文件提取”方法(主要是在Big Data中)是将原始文件保存为 gzip格式,并使用流算法进行传输,这种方法可以快速运行并且在unix管道中不会消耗磁盘:
gunzip remote_or_local_file.csv.gz | convert_to_sql | psql
因此,理想(未来)是format 的服务器选项.csv.gz
。