2016-10-26 1 views
0

У меня есть код, который вставляет некоторые данные в мою базу данных из файла csv. Сейчас он работает довольно медленно. Я смотрел в другом месте, и казалось, что использование COPY может помочь ускорить мои вставки, но (как вы можете видеть ниже) мне иногда нужно обновить db, так как я не хочу дубликатов. Кроме того, почти все данные основаны на подзапросах SELECT. Вот соответствующая часть моего кода:Как эффективно вставлять и обновлять данные в базу данных с помощью psychopg2?

def insert_values(self): 
    read_dict=self.my_csv_reader.main_row_dict 
    for key in read_dict: 
     for row_index in range(1, len(read_dict[key])): 
      if read_dict[key][row_index]==None or read_dict[key][row_index]=="" or read_dict[key][row_index]==" ": 
       continue 
      else: 
       self.cur.execute(self.build_sub_select_query(self.ID, "wf_variable", "name = " + "'" + self.get_header_value(row_index) + "'")) 
       wf_variable_id=self.cur.fetchall()[self.QUERY_LIST_INDEX][self.QUERY_TUPLE_INDEX] 
       self.cur.execute(self.build_sub_select_query(self.ID, "point", "guid =" + "'" + read_dict[key][0] + "'")) 
       point_id =self.cur.fetchall()[self.QUERY_LIST_INDEX][self.QUERY_TUPLE_INDEX] 
       self.cur.execute(self.build_sub_select_query("point."+self.ID,"point join point_data on point.id=point_data.point_id","point.id = " + str(point_id) +" and wf_variable_id = "+ str(wf_variable_id))) 
       id_count = len(self.cur.fetchall()[0]) 
       if id_count==0: 
        sql_code = "INSERT INTO " + self.TABLE + "(value, point_id, wf_variable_id) VALUES " + " (%s,%s,%s)" 
        self.cur.execute(sql_code,(read_dict[key][row_index],point_id,wf_variable_id)) 
       elif id_count==1: 
        sql_code = "UPDATE " + self.TABLE + " set " + "value=%s where point_id=%s and wf_variable_id =%s" 
        self.cur.execute(sql_code, (read_dict[key][row_index], point_id, wf_variable_id)) 
       else: 
        Exception("The point with id, "+point_id+" has more than one parent level attribute. This should be investigated.") 

Я задаюсь быстрый способ вставки/обновления данных из моего файла CSV. Должен ли я сначала запускать все мои подзапросы и хранить результаты в структуре данных? Точно так же, как только я узнаю, должен ли запрос быть вставкой и обновлением, я должен хранить их отдельно и запускать их с помощью функции executeemany(), вместо того, чтобы использовать execute повторно или я неправильно понимаю цель executeemany? Как замечание, я понимаю, что есть другие улучшения, которые я мог бы внести в код, например, разбить вставку и обновление на отдельные функции, и я буду реорганизовать, как только я получу эту работу, но сейчас я просто пытаюсь обратиться к вопрос скорости.

ответ

0

На всякий случай, если кто-нибудь столкнется с этим, я опубликую, какие шаги я предпринял для повышения эффективности. В общем, наиболее важными шагами были минимизация количества вызовов в postgres db.

Я сделал очень простой профилирование (то есть положил time.time() в верхней части моего цикла, а затем усреднил несколько итераций, чтобы попытаться выяснить, какие части занимают самые длинные). Я обнаружил, что удаление даже одного SELECT из цикла сокращает секунду в среднем на 5,6 сек (почти 20% экономии). Очевидно, что вызовы db являются доминирующим действием. Имея это в виду, я учитывал SELECT как можно лучше.

Поскольку первый SELECT (тот, который использовался для поиска wf_variable_id), просто просмотрел идентификатор переменной, я смог сохранить это в списке. Я был пакетным обновлением одного и того же набора переменных, а имена переменных были в заголовке моего csv, поэтому мне нужно было только сделать этот вызов по одной переменной, а не для каждой строки моего csv. Иногда я обновлял одну и ту же точку, поэтому я сохранил их в dict и сначала проверил, был ли там мой идентификатор точки перед выполнением SELECT и добавлением его в словарь (память может быть ограничивающим фактором для других людей, просматривающих эту запись, но для меня это не было). Это сбрит примерно на секунду. Итак, для всех, кто натыкается на это сообщение, везде, где это возможно, удаляет запросы и предпочитает хранить значения в памяти.

+0

Последнее замечание, я реорганизовал цикл так, чтобы он пересекал все, что мне нужно, чтобы выбрать в моем csv и кормить его в кортеж. Затем я выбрал все это сразу, а не один за другим. Это значительно улучшило производительность. Например, при одном тестовом прогоне в 500 строк потребовалось около 30 секунд, чтобы выполнить один большой запрос, в отличие от примерно 0,5 секунды на небольшой запрос (всего 250 секунд или примерно 8 раз больше времени). Снова урок что минимизация запросов является ключом к ускорению сценариев, использующих psychopg. –

Смежные вопросы