Использование COPY FROM stdin для загрузки таблиц, чтение входного файла только один раз - PullRequest
4 голосов
/ 05 марта 2011

У меня есть большой (~ 60 миллионов строк) исходный файл фиксированной ширины с ~ 1800 записями на строку.

Мне нужно загрузить этот файл в 5 разных таблиц на экземпляре Postgres 8.3.9.

Моя дилемма в том, что, поскольку файл очень большой, мне бы хотелось прочитать его только один раз.

Это достаточно просто, используя INSERT или COPY в обычном режиме, но япытаюсь повысить скорость загрузки, включив мои операторы COPY FROM в транзакцию, которая включает в себя TRUNCATE - избегая ведения журнала, что должно значительно повысить скорость загрузки (в соответствии с http://www.cirrusql.com/node/3)., насколько я понимаю,, вы можете отключить вход в Postgres 9.x - но у меня нет этой опции на 8.3.9.

В приведенном ниже скрипте я дважды читаю входной файл, чего я хочу избежать ...Любые идеи о том, как я мог бы сделать это, читая входной файл только один раз? Не нужно быть Bash - я также пытался использовать psycopg2, но не мог понять, как поток вывода файла в оператор COPY, как яделать бытьнизкий.Я не могу скопировать файл, потому что мне нужно проанализировать его на лету.

#!/bin/bash

table1="copytest1"
table2="copytest2"

#note: $1 refers to the first argument used when invoking this script
#which should be the location of the file one wishes to have python
#parse and stream out into psql to be copied into the data tables

( echo 'BEGIN;'
  echo 'TRUNCATE TABLE ' ${table1} ';'
  echo 'COPY ' ${table1} ' FROM STDIN'
  echo "WITH NULL AS '';"
  cat $1 | python2.5 ~/parse_${table1}.py 
  echo '\.'
  echo 'TRUNCATE TABLE ' ${table2} ';'
  echo 'COPY ' ${table2} ' FROM STDIN'
  echo "WITH NULL AS '';"
  cat $1 | python2.5 ~/parse_${table2}.py 
  echo '\.'
  echo 'COMMIT;'
) | psql -U postgres -h chewy.somehost.com -p 5473 -d db_name

exit 0

Спасибо!

Ответы [ 2 ]

2 голосов
/ 06 марта 2011

Вы можете использовать именованные каналы вместо своего анонимного канала. С этой концепцией ваш скрипт на python может заполнять таблицы через различные процессы psql соответствующими данными.

Создание труб:

mkfifo fifo_table1
mkfifo fifo_table2

Запускать экземпляры psql:

psql db_name < fifo_table1 &
psql db_name < fifo_table2 &

Ваш скрипт на Python будет выглядеть примерно так (псевдокод):

SQL_BEGIN = """
BEGIN;
TRUNCATE TABLE %s;
COPY %s FROM STDIN WITH NULL AS '';
"""
fifo1 = open('fifo_table1', 'w')
fifo2 = open('fifo_table2', 'w')

bigfile = open('mybigfile', 'r')

print >> fifo1, SQL_BEGIN % ('table1', 'table1') #ugly, with python2.6 you could use .format()-Syntax     
print >> fifo2, SQL_BEGIN % ('table2', 'table2')      

for line in bigfile:
  # your code, which decides where the data belongs to
  # if data belongs to table1
  print >> fifo1, data
  # else
  print >> fifo2, data

print >> fifo1, 'COMMIT;'
print >> fifo2, 'COMMIT;'

fifo1.close()
fifo2.close()

Возможно, это не самое элегантное решение, но оно должно работать.

2 голосов
/ 05 марта 2011

Зачем использовать COPY для второй таблицы?Я бы предположил, что выполнение:

INSERT INTO table2 (...)
SELECT ...
FROM table1;

будет быстрее, чем использование COPY.

Редактировать Если вам нужно импортировать разные строки в разные таблицы, но из одного и того же исходного файла, возможно, вставьте все в промежуточную таблицу, а затем вставьте строки оттуда в целевые таблицы:

Импортируйте. весь текстовый файл * в одну промежуточную таблицу:

COPY staging_table FROM STDIN ...;

После этого шага весь входной файл находится в staging_table

Затем скопируйте строки из промежуточной таблицы вотдельные целевые таблицы, выбирая только те, которые соответствуют требованиям соответствующей таблицы:

INSERT INTO table_1 (...)
SELECT ...
FROM staging_table
WHERE (conditions for table_1);

INSERT INTO table_2 (...)
SELECT ...
FROM staging_table
WHERE (conditions for table_2);

Это, конечно, возможно, только если в вашей базе данных достаточно места для хранения промежуточной таблицы.

...