Как КОПИРОВАТЬ CSV как поля JSON

Есть ли способ КОПИРОВАТЬ данные файла CSV непосредственно в массив JSON или JSONb?

Пример:

CREATE TABLE mytable (
    id serial PRIMARY KEY,
    info jSONb -- or JSON
);
COPY mytable(info) FROM '/tmp/myfile.csv' HEADER csv;

ПРИМЕЧАНИЕ: каждая строка CSV сопоставляется с массивом JSON. Это обычный CSV.


Обычный CSV (без встроенного JSON)... /tmp/myfile.csv =

a,b,c
100,Mum,Dad
200,Hello,Bye

Правильная команда COPY должна быть эквивалентна обычной команде копирования ниже.

Обычный COPY (некрасиво, но работает нормально)

CREATE TEMPORARY TABLE temp1 (
  a int, b text, c text
);
COPY temp1(a,b,c) FROM '/tmp/myfile.csv' HEADER csv;

INSERT INTO mytable(info) SELECT json_build_array(a,b,c) FROM temp1;

Это некрасиво, потому что:

  • нужны априорные знания о полях и предыдущее CREATE TABLE с ним.

  • для «больших данных» нужна большая временная таблица, поэтому потерянный ЦП, диск и мое время таблица mytable имеет ограничения CHECK и UNIQUE для каждой строки.

  • ... Требуется более 1 команды SQL.


person Peter Krauss    schedule 04.01.2017    source источник
comment
См. раздел stackoverflow.com/questions/10824165/   -  person Jim Moore    schedule 04.01.2017
comment
Покажите нам содержимое файла. Но если он содержит действительные литералы JSON тогда это должно работать. Что произошло, когда вы попытались?   -  person a_horse_with_no_name    schedule 04.01.2017
comment
@a_horse_with_no_name спасибо. Что ж, это работает, но не элегантно (я использую большие данные), нужны некоторые шаги и временная таблица... Я редактировал, см. пример.   -  person Peter Krauss    schedule 04.01.2017
comment
Спасибо @JimMoore, да, с помощью внешней программы мы можем делать все что угодно ... Мой вопрос о том, как это сделать с PostgreSQL 9.6+.   -  person Peter Krauss    schedule 04.01.2017
comment
Вы можете избежать вставки в таблицу с помощью оболочки сторонних данных file_fdw и читать данные непосредственно из файла. Хотя это по-прежнему требует предварительных знаний о столбцах.   -  person JosMac    schedule 04.01.2017
comment
@JosMac, думаю, это будет хороший ответ! Временная таблица - большая проблема... Элегантность и априорность знания я могу не заметить ;-)   -  person Peter Krauss    schedule 04.01.2017


Ответы (1)


Идеальное решение!

Не нужно знать все столбцы CSV, извлекайте только то, что знаете.

Используйте в SQL CREATE EXTENSION PLpythonU;: если команда выдает ошибку типа "не удалось открыть файл управления расширением... Нет такого файла", вам необходимо установить дополнительные пакеты pg-py. В стандартном UBUNTU (16 LTS) просто, apt install postgresql-contrib postgresql-plpython.

CREATE FUNCTION get_csvfile(
  file text,
  delim_char char(1) = ',',
  quote_char char(1) = '"')
returns setof text[] stable language plpythonu as $$
  import csv
  return csv.reader(
     open(file, 'rb'),
     quotechar=quote_char,
     delimiter=delim_char,
     skipinitialspace=True,
     escapechar='\\'
  )
$$;

INSERT INTO mytable(info)
  SELECT jsonb_build_array(c[1],c[2],c[3]) 
  FROM get_csvfile('/tmp/myfile1.csv') c;

Функция split_csv() была определена здесь. csv.reader очень надежен (!).

Не тестировался для большого-большого CSV... Но ожидается, что Python сделает свою работу.


Обходной путь PostgreSQL

Это не идеальное решение, но оно решает основную проблему, т.

... большая временная таблица, так что потерян процессор, диск и мое время"...

Вот как мы это делаем, обходной путь с file_fdw!

  1. Примите свои соглашения, чтобы избежать путаницы с копированием файлов и правами доступа к файлам... Стандартный путь к файлу для CSV. Пример: /tmp/pg_myPrj_file.csv

  2. Инициализируйте свою базу данных или сценарий SQL с расширением magic,

   CREATE EXTENSION file_fdw;
   CREATE SERVER files FOREIGN DATA WRAPPER file_fdw;
  1. Для каждого CSV-файла myNewData.csv,

    3.1. сделать символическую ссылку (или scp удаленную копию) для вашего нового файла ln -sf $PWD/myNewData.csv /tmp/pg_socKer_file.csv

    3.2. настройте file_fdw для вашей новой таблицы (допустим, mytable).

   CREATE FOREIGN TABLE temp1 (a int, b text, c text) 
   SERVER files OPTIONS ( 
     filename '/tmp/pg_socKer_file.csv', 
       format 'csv', 
       header 'true'
   );

PS: после запуска SQL-скрипта с psql при возникновении проблем с правами смените владельца ссылки на sudo chown -h postgres:postgres /tmp/pg_socKer_file.csv.

3.3. использовать таблицу file_fdw в качестве источника (предположим, заполнить mytable).

 INSERT INTO mytable(info)
 SELECT json_build_array(a,b,c) FROM temp1;

Спасибо @JosMac (и его руководству)!


ПРИМЕЧАНИЕ: если есть способ STDIN сделать это (существует??), будет легко, избегая проблем с правами доступа и использования абсолютных путей. См. этот ответ/обсуждение.

person Community    schedule 04.01.2017
comment
Чтобы узнать о правах доступа к файлам через ACL, см. это решение. - person Peter Krauss; 12.01.2017
comment
Или просто разделите CSV символами, которых нет ни в JSON, ни в CSV... adpgtech.blogspot.co.uk/2014/09/importing-json-data.html - person Daniel Cull; 19.01.2018
comment
Привет @DanielCull, хороший обходной путь. По поводу основной проблемы, производительности (затраты процессорного времени и памяти), единственное решение FOREIGN TABLE. Что касается другого контекста или условий, да, возможно использовать csv quote e'\x01' delimiter e'\x02'; во внутренних процедурах, но не как общедоступный обмен данными (например, см. стандарты W3C для семантического CSV или frictionlessdata.io стандарты обмена). - person Peter Krauss; 20.01.2018
comment
спасибо, это работает, но вы забыли псевдоним c FROM get_csvfile('/tmp/myfile1.csv') c - person bormat; 19.06.2018
comment
Привет @bormat, пожалуйста, отредактируй (!) непосредственно текст ответа, это вики - person Peter Krauss; 19.06.2018
comment
верно. Я не привык редактировать пост, где я не автор. - person bormat; 20.06.2018
comment
Извините @bormat, вы добавили 2 символа в тело, исправив это (!)... Я удаляю эти комментарии. - person Peter Krauss; 20.06.2018