Відповіді:
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 Мб також може допомогти.
UNNEST
Функція з масивами може використовуватися разом із синтаксисом багатозначних VALUES. Я думаю, що цей метод є більш повільним, ніж використання, COPY
але він корисний мені в роботі з psycopg та python (python list
переданий cursor.execute
стає pg ARRAY
):
INSERT INTO tablename (fieldname1, fieldname2, fieldname3)
VALUES (
UNNEST(ARRAY[1, 2, 3]),
UNNEST(ARRAY[100, 200, 300]),
UNNEST(ARRAY['a', 'b', 'c'])
);
без VALUES
використання підселектора з додатковою перевіркою існування
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 формати ". Робіть це лише в тому випадку, якщо у вас є мільйони рядків для вставки та якщо вам зручно використовувати бінарні дані.
Ось приклад рецепту в Python, використовуючи psycopg2 з двійковим входом .
В основному це залежить від (іншої) діяльності в базі даних. Такі операції ефективно заморожують всю базу даних для інших сеансів. Інша увага - це модель даних та наявність обмежень, тригерів тощо.
Мій перший підхід - це завжди: створити таблицю (temp) зі структурою, подібною до цільової таблиці (створити таблицю tmp AS select * з target, де 1 = 0), і почати з читання файлу в таблиці temp. Потім я перевіряю, що можна перевірити: дублікати, ключі, які вже є у цілі тощо.
Тоді я просто роблю "вставку в цільовий вибір * від tmp" або подібне.
Якщо це не вдається або триває занадто довго, я перериваю це і розглядаю інші методи (тимчасово відміняючи індекси / обмеження тощо)
Я реалізував дуже швидкий завантажувач даних Postgresq за допомогою власних методів libpq. Спробуйте мій пакет https://www.nuget.org/packages/NpgsqlBulkCopy/
Я щойно стикався з цим питанням і рекомендую csvsql ( релізи ) для масового імпорту в Postgres. Щоб виконати об'ємну вставку, яку ви просто createdb
використаєте csvsql
, а потім скористайтеся , яка підключається до вашої бази даних та створює окремі таблиці для всієї папки CSV-файлів.
$ createdb test
$ csvsql --db postgresql:///test --insert examples/*.csv
Термін "масові дані" пов'язаний з "великою кількістю даних", тому цілком природно використовувати оригінальні необроблені дані , не потребуючи перетворення їх у SQL. Типові файли необроблених даних для "масової вставки" - це CSV та JSON формати .
У програмах ETL та процесах прийому всередину нам потрібно змінити дані перед їх вставкою. Тимчасовий стіл займає (багато) місця на диску, і це не найшвидший спосіб зробити це. Обгортка зовнішніх даних PostgreSQL (ІДП) є найкращим вибором.
CSV приклад . Припустимо, tablename (x, y, z)
на 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
Тож ідеал (майбутнє) - це серверний варіант для формату .csv.gz
.