Как обновить выбранные строки значениями из файла CSV в Postgres? - PullRequest
61 голосов
/ 18 января 2012

Я использую Postgres и хотел бы сделать большой запрос на обновление, который можно было бы получить из файла CSV, скажем, у меня есть таблица с (id, banana, apple).

Я хотел бы выполнитьобновление, которое изменяет бананы, а не яблоки, каждый новый банан и их ID будут в файле CSV.

Я пытался просмотреть сайт Postgres, но примеры меня убивают.

Ответы [ 2 ]

130 голосов
/ 18 января 2012

Я бы COPY файл во временную таблицу и обновил фактическую таблицу оттуда.Может выглядеть так:

CREATE TEMP TABLE tmp_x (id int, apple text, banana text); -- but see below

COPY tmp_x FROM '/absolute/path/to/file' (FORMAT csv);

UPDATE tbl
SET    banana = tmp_x.banana
FROM   tmp_x
WHERE  tbl.id = tmp_x.id;

DROP TABLE tmp_x; -- else it is dropped at end of session automatically

Если импортируемая таблица соответствует таблице, которая должна быть точно обновлена, это может быть удобно:

CREATE TEMP TABLE tmp_x AS SELECT * FROM tbl LIMIT 0;

Создает пустую временную таблицу, соответствующую структуресуществующая таблица, без ограничений.

Привилегии

SQL COPY для этого требуются привилегии суперпользователя.( Руководство ):

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

Метакоманда psql \copy работает для любой роли БД. Руководство:

Выполняет копию клиентского приложения.Эта операция запускает команду SQL COPY , но вместо чтения или записи указанного файла сервером psql читает или записывает файл и направляет данные между сервером и локальной файловой системой.Это означает, что доступность файлов и привилегии принадлежат локальному пользователю, а не серверу, и привилегии суперпользователя SQL не требуются.

Область действия временных таблиц ограничена одним сеансом одной роли, так что вышеупомянутое должно быть выполнено в том же сеансе psql:

CREATE TEMP TABLE ...;
\copy tmp_x FROM '/absolute/path/to/file' (FORMAT csv);
UPDATE ...;

Если вы пишете это в команде bash, обязательно оберните все это в один PSQL вызов.Например:

echo 'CREATE TEMP TABLE tmp_x ...; \copy tmp_x FROM ...; UPDATE ...;' | psql

Обычно вам нужна мета-команда \\ для переключения между мета-командами psql и SQL-командами в psql, но \copy является исключением из этого правила.Руководство снова:

специальные правила синтаксического анализа применяются к мета-команде \copy.В отличие от большинства других мета-команд, весь остаток строки всегда принимается за аргументы \copy, и в аргументах не выполняется ни интерполяция переменных, ни расширение обратных кавычек.

Большие таблицы

Если таблица импорта велика, то может быть временно увеличено temp_buffers для сеанса (первое в сеансе):

SET temp_buffers = '500MB';  -- example value

Добавить индекс во временную таблицу:

CREATE INDEX tmp_x_id_idx ON tmp_x(id);

И запустить ANALYZE вручную, так как временные таблицы не охватываются автоочисткой / автоанализом.

ANALYZE tmp_x;

Связанные ответы:

0 голосов
/ 26 сентября 2018

Вы можете попробовать приведенный ниже код, написанный на python, входной файл - это файл csv, содержимое которого вы хотите обновить в таблицу. Каждая строка разделяется на основе запятой, поэтому для каждой строки строка [0] - это значение в первом столбце, строка [1] - это значение во втором столбце и т.д.

    import csv
    import xlrd
    import os
    import psycopg2
    import django
    from yourapp import settings
    django.setup()
    from yourapp import models


    try:
       conn = psycopg2.connect("host=localhost dbname=prodmealsdb 
       user=postgres password=blank")
       cur = conn.cursor()

       filepath = '/path/to/your/data_to_be_updated.csv'
       ext = os.path.splitext(filepath)[-1].lower()
       if (ext == '.csv'): 
          with open(filepath) as csvfile:
          next(csvfile)
          readCSV = csv.reader(csvfile, delimiter=',')
          for row in readCSV:
              print(row[3],row[5])
              cur.execute("UPDATE your_table SET column_to_be_updated = %s where 
              id = %s", (row[5], row[3]))
              conn.commit()
          conn.close()
          cur.close()

    except (Exception, psycopg2.DatabaseError) as error:
    print(error)
    finally:
    if conn is not None:
      conn.close()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...