Хорошо!
Таким образом, ответ таков: я вставлял каждую строку в отдельности и выполнял круговое отключение в БД для каждой проверки адреса. Проверка адреса была худшей частью, так как она стала экспоненциально медленнее. Я рассчитал, что вставка исходных данных (1,5 часа), а затем вставка тех же данных снова займет ~ 9 часов!
Таким образом, этот ответ будет касаться того, что я сделал для преобразования в массовые операторы вставки, а также некоторых моментов, на которые следует обратить внимание.
- ORM в sqlalchemy «поможет»
ORM великолепен, но имейте в виду, что он плохо сочетается с объемными вставками. Для массовой вставки требуется использовать операторы нижнего уровня execute
в сеансе. Они принимают не объекты ORM в качестве входных данных, а список словарей и объект insert
. Поэтому, если вы конвертируете CSV-файл, полный строк, в объекты ORM, вам нужно НЕ добавить их в текущий сеанс, а вместо этого преобразовать их в словари для дальнейшего использования.
def asdict(obj):
return dict((col.name, getattr(obj, col.name))
for col in class_mapper(obj.__class__).mapped_table.c)
currGUID = uuid.uuid4()
currPrintOrMail = printOrMail(rec, id=currGUID)
currStatement = statements(rec, id=currGUID)
currAddress = self.get_or_create(address, rec)
currStatement.address = currAddress
self.currPrintOrMail_bulk.append(asdict(currPrintOrMail))
self.currStatement_bulk.append(asdict(currStatement))
Метод asdict происходит от здесь . Это дает вам словари столбцов в созданных объектах ORM. Они никогда не добавляются в сеанс и вскоре после этого выпадают из памяти.
- Отношения будут кусать вас
Если вы установили отношения ORM:
class statements(Base):
__tablename__ = 'statements'
id = id_column()
county = Column(String(50),default='',nullable=False)
address_id = Column(CHAR(36), ForeignKey('address.id'))
address = relationship("address", backref=backref("statements", cascade=""))
printOrMail_id = Column(CHAR(36), ForeignKey('printOrMail.id'))
pom = relationship("printOrMail", backref=backref("statements", cascade=""))
property_id = Column(CHAR(36), ForeignKey('property.id'))
prop = relationship("property", backref=backref("statements", cascade=""))
Убедитесь, что каскад пустой в обратном ссылочном коде ! В противном случае вставка объекта в отношение в сеанс будет каскадным через остальные объекты. Когда вы попытаетесь выполнить массовую вставку ваших значений позже, они будут отклонены как дубликаты ... если вам повезет.
Это важно, потому что частью требований было получение address_id для действительного адреса, если он существовал, и добавление адреса, если его не было. Поскольку отключение запроса было слишком медленным, я изменил get_or_create
на:
def get_or_create(self, model, rec):
"""Check if current session has address. If not, query DB for it. If no one has the address, create and flush a new one to the session."""
instance = self.session.query(model).get((rec['Name'], rec['Address_Line_One'], rec['Address_Line_Two'], rec['Address_Line_Three'], rec['Address_Line_Four']))
if instance:
return instance
else:
instance = model(rec)
self.session.add(instance)
self.session.flush()
return instance
Использование get
заставляет sqlalchemy сначала проверять сеанс, предотвращая поездки по сети. Но это работает, только если к сеансу добавлено новых адресов! Помните отношения? Это каскадно вставлялось в заявления. Кроме того, если у вас нет flush()
или autoflush=True
, get
не может видеть вновь добавленные объекты.
Когда вы создаете сеанс, сохраняйте ваши объекты!
self.session = sessionmaker (autoflush = False, expire_on_commit = False)
Если вы не включите expire_on_commit=False
, тогда вы потеряете свои адреса и снова начнете совершать обходы.
- ORM объекты имеют вставку
Теперь у нас есть список словарей для вставляемых объектов ORM. Но нам также нужен объект вставки.
self.session.execute(printOrMail.__table__.insert(), self.currPrintOrMail_bulk)
self.session.execute(statements.__table__.insert(), self.currStatement_bulk)
Похоронен в документах , кажется, что можно использовать classname.__table__
для необходимого табличного объекта, требуемого insert . Поэтому в сеансе, используя класс ORM, чтобы получить таблицу для получения объекта вставки, выполните команду execute со списком словарей. Не забывайте совершать потом!
- Не хватает памяти
Это позволит вам успешно смешивать массовую вставку и ORM со связями и запросами уникальных записей в sqlalchemy. Просто следите за нехваткой памяти. Мне приходилось массово вставлять ~30,000
записей за раз, в противном случае py2.7(32bit)
зависал бы на 2G
.