Как ускорить вставку при проверке ограничений и возврате значений с помощью python или postgres - PullRequest
0 голосов
/ 09 мая 2018

Я пытаюсь ускорить вставки в схему. Я уже видел некоторые вопросы по этому поводу, но они не касаются моей проблемы, они наиболее актуальны:

Все таблицы в моей схеме содержат автоматически сгенерированные ключи Serial, поэтому я использую RETURNING id в конце вставок. Причина, по которой я не использую copy, заключается в том, что мне нужно проверить наличие ограничений. В частности, некоторые поля имеют уникальные индексы, поэтому я проверяю, существует ли конкретная строка, чтобы я мог выбрать ее идентификатор (для сопоставления автоматически сгенерированных ключей)

Я делаю это:

sql = "INSERT INTO %s.%s(%s) VALUES (%s) ON CONFLICT DO NOTHING RETURNING %s;"
sql = sql % (schema_name,table_name,','.join(head[1:]),'%s',head[0])

где head содержит название полей в таблице. Затем я делаю это:

    try:
        # get the auto generated id back
        return_id = pdb.cur.fetchone()[0]     
    except TypeError:
        sql_exist_id = "SELECT %s FROM %s.%s WHERE %s = '%s'" % (head[0],schema_name,table_name,unique_field,row[1])

У меня такой вопрос: есть ли способ выполнить пакетную вставку (например, psycopg2: вставка нескольких строк одним запросом ), при проверке ограничений и возврате ключей?

1 Ответ

0 голосов
/ 27 мая 2018

Вы можете быть довольно близко ...

Предложение RETURNING будет работать со вставками с несколькими значениями, например:

insert into some_table (col1, col2) values 
    (val1, val2), (val3, val4), 
    (val5, val6), (val7, val8) 
    returning id 

Предполагая, что some_table имеет serial, вы получите 4 результата от этого заявление. Также будет работать с insert into ... select:

insert into some_table (col1, col2) 
    select col1, col2 
      from some_other_source returning id

Но в обоих случаях, если вы укажете on conflict do nothing, весь оператор потерпит неудачу в случае конфликта, поэтому одна плохая запись убивает транзакцию.

Но учтите, что returning также работает с удалениями:

delete from some_table where col1 = 'bob' returning id

Соединение с предложением от @JacobH: массовая загрузка в темп таблица, отфильтруйте конфликтующие строки, затем вставьте оставшиеся, не конфликтующие строк. Вы упомянули copy, так что я предполагаю, что уже есть .csv, что примерно соответствует месту назначения. Примерно так:

with conn.cursor() as stmt:
    #create a constraint-free temp table based on your destination table
    #the `where 0 = 1` ensures it's empty
    stmt.execute('select * into temp tmp_some_table from some_table where 0 = 1') 

    #bulk load into the temp table:
    with open('some.csv') as infile:
        stmt.copy_from(infile, 'tmp_some_table', sep=',', columns=['col1', 'col2'])

    #strip out your conflicts. note that we are returning col1 (assumed to be a unique key)
    #you can do as many passes as needed...
    stmt.execute('delete from tmp_some_table tst using some_table st where tst.col1 = st.col1 returning col1')
    dupe_keys = stmt.fetchall() #all your deleted, conflicting keys

    #bulk insert non-conflicting values
    stmt.execute('insert into some_table (col1, col2) select (col1, col2) from tmp_some_table returning id') 
    inserted_ids = stmt.fetchall() #all the ids created from the insert

Бонус в том, что это должно быть очень быстро по сравнению с повторением и вызовом execute - по сути, вы делаете все в 4 заявлениях; основная часть сети ввода / вывода находится на том первоначальном импорте CSV против N циклов на N вставках записей. Все остальное происходит в базе данных. Временная таблица, вероятно, останется в памяти, если она достаточно мала.

Поймите, я опоздал на этот ответ, но я надеюсь, что это поможет.

...