Как обновить записи в SQL Alchemy в цикле - PullRequest
2 голосов
/ 05 февраля 2011

Я пытаюсь использовать SQLSoup - расширение SQLAlchemy для обновления записей в базе данных SQL Server 2008.Я использую pyobdc для соединений.Есть ряд проблем, которые затрудняют поиск подходящего примера.

Я перепроецирую геометрическое поле в очень большой таблице (более 2 миллионов записей), поэтому многие стандартные способы обновления полей использовать нельзя.Мне нужно извлечь координаты из поля геометрии в текст, преобразовать их и передать обратно. Все это хорошо, и все отдельные части работают.

Однако я хочу выполнить инструкцию SQL Update для каждой строки, одновременно просматривая записи по очереди.Я предполагаю, что это блокирует набор записей или соединение используется - как будто я использую приведенный ниже код, который зависает после успешного обновления первой записи.

Любой совет о том, как создать новое соединение, повторно использовать существующее или выполнить это другим способом, приветствуется.

s = select([text("%s as fid" % id_field),
            text("%s.STAsText() as wkt" % geom_field)],
           from_obj=[feature_table])

rs = s.execute()

for row in rs:
    new_wkt = ReprojectFeature(row.wkt)

    update_value = "geometry :: STGeomFromText('%s',%s)" % (new_wkt, "3785")
    update_sql = ("update %s set GEOM3785 = %s where %s = %i" %
                  (full_name, update_value, id_field, row.fid))

    conn = db.connection()
    conn.execute(update_sql)
    conn.close() #or not - no effect..

Обновленный рабочий код теперь выглядит следующим образом.Он отлично работает на нескольких записях, но зависает на всей таблице, поэтому я думаю, что он читает слишком много данных.

db = SqlSoup(conn_string)
#create outer query

Session = sessionmaker(autoflush=False, bind=db.engine)
session = Session()
rs = session.execute(s)

for row in rs: 
    #create update sql...
    session.execute(update_sql)
session.commit()

Теперь я получаю сообщения об ошибках соединения.

DBAPIError: (Ошибка) ('HY000', '[HY000] [Microsoft] [Драйвер ODBC SQL Server] Соединение установленозанят результатами для другого hstmt (0) (SQLExecDirectW) ')

Похоже, это может быть проблема с драйвером ODBC - http://sourceitsoftware.blogspot.com/2008/06/connection-is-busy-with-results-for.html

Дальнейшее обновление:

На сервере, использующем профилировщик, он показывает оператор выбора, после чего первый оператор обновления «запускается», но не завершается.Если я задаю оператор Select для возврата первых 10 строк, то он завершается и обновления запускаются.

SQL: Batch Starting   Select...
SQL: Batch Starting   Update...

Я думаю, что это проблема с pyodbc и драйверами SQL Server.Если я удаляю SQL Alchemy и выполняю тот же SQL с pyodbc, он также зависает.Даже если я создаю новый объект подключения для обновлений.

Я также попробовал драйвер SQL Server Native Client 10.0, который позволяет использовать MARS - множественных активных наборов записей , но это не имело никакого значения.В конце я прибег к «разбивке по страницам результатов» и обновлению этих пакетов с использованием pyodbc и SQL (см. Ниже), однако я подумал, что SQLAlchemy сможет сделать это для меня автоматически.

Ответы [ 3 ]

1 голос
/ 06 февраля 2011

Могу ли я предложить, что, когда ваш процесс зависает, вы делаете sp_who2 в поле Sql и смотрите, что происходит. Проверьте наличие заблокированных spid и посмотрите, сможете ли вы найти в коде Sql что-нибудь, что может подсказать, что происходит. Если вы нашли спид, который блокирует других, вы можете сделать dbcc inputbuffer(*spidid*) и посмотреть, говорит ли он, какой запрос был выполнен. В противном случае вы также можете прикрепить Sql-профилировщик и отслеживать ваши звонки.

В некоторых случаях это может также быть параллелизм на сервере Sql, который вызывает блокировку. Если это не хранилище данных, я предлагаю отключить Max DOP (установите его на 1). Дайте мне знать, и когда я проверю это снова утром, и вам понадобится помощь, я буду рад помочь.

1 голос
/ 06 февраля 2011

Пока я не найду другое решение, я использую одно соединение и пользовательский SQL для возврата наборов записей и обновляю их пакетами. Я не думаю, что то, что я делаю, является особенно уникальным случаем, поэтому я не уверен, почему я не могу обрабатывать несколько наборов результатов одновременно.

Ниже работает, но очень, очень медленно ..

cnxn = pyodbc.connect(conn_string, autocommit=True)
cursor = cnxn.cursor()

#get total recs in the database
s = "select count(fid) as count from table"
count = cursor.execute(s).fetchone().count

#choose number of records to update in each iteration
batch_size = 100
for i in range(1,count, batch_size):
    #sql to bring back relevant records in each batch
    s = """SELECT fid, wkt from(select ROW_NUMBER() OVER(ORDER BY FID ASC) AS 'RowNumber'
,FID
,GEOM29902.STAsText() as wkt
    FROM %s) features
    where RowNumber >= %i and RowNumber <= %i""" % (full_name,i,i+batch_size)

    rs = cursor.execute(s).fetchall()
    for row in rs:
        new_wkt = ReprojectFeature(row.wkt)
        #...create update sql statement for the record
        cursor.execute(update_sql)
        counter += 1
cursor.close()
cnxn.close()  
1 голос
/ 05 февраля 2011

Попробуйте использовать Session .

rs = s.execute(), затем становится session.execute(rs), и вы можете заменить последние три строки на session.execute(update_sql).Я бы также предложил настроить сеанс с отключенной автоматической фиксацией и в конце вызвать session.commit().

...