SQLAlchemy обновляет несколько строк в одной транзакции - PullRequest
0 голосов
/ 25 января 2019

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

У меня есть таблица:

class MyTable(BaseModel):
    col1 = sa.Column(sa.String(256))
    col2 = sa.Column(sa.String(256))

Учитывая, что col1 уже имеет значения и col2 пусто, как я могу обновить col2, если у меня есть набор данных в качестве словаря:

payload = {'x': 'y', 'a': 'b', 'c': 'd'}

Итак, эти карты полезной нагрузкизначения для col1, для новое значение для col2;после обновления вы получите [{'col1': 'x', 'col2': 'y'}, ...] из базы данных.

Я попробовал несколько способов, которые на самом деле работают, но я думаю, что они не так оптимальны, как это могло бы быть, например: * 10101

my_table = MyTable.__table__
for key, value in payload.items():
    stm = my_table.update()
    stm = stm.where(getattr(sales_order_item.c, 'col1') == key)
    stm = stm.values({'col2': value})
    session.execute(stm)

Или вот так

for key, value in payload.items():
    query = session.query(MyTable).filter(MyTable.col1==key)
    query.update({MyTable.col2: value})

Теперь оба эти решения работают, как и ожидалось, единственное, что меня беспокоит, это время, которое требуется, например, для полезной нагрузки в 100 элементов, которая занимает до 6секунд, и я почти уверен, что должен быть лучший способ сделать это, не так ли?

Я думал, есть ли способ заставить его работать с функцией in_:

query(MyTable).filter(
        MyTable.col1.in_(payload.keys())
    )

но я не знаю, как структурировать запрос на обновление.

Ответы [ 2 ]

0 голосов
/ 25 января 2019

Другое решение, которое я нашел перед выбранным ответом, которое также работает быстро, будет:

# payload = {'x': 'y', 'a': 'b', 'c': 'd'}
all_rows = query(MyTable).filter(
    MyTable.col1.in_(payload)
)
for row in all_rows:
    row.col2=payload[row.col1]

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

0 голосов
/ 25 января 2019

Да, обновление большего числа строк одним оператором UPDATE будет намного быстрее, чем использование отдельных UPDATE s для каждого объекта.Фильтр IN поможет вам ограничить количество обновляемых строк, но вам все равно нужно указать базе данных, какое значение использовать для обновлений col2.

Для этого можно использовать конструкцию CASE ... WHEN ... THEN., с помощью функции case() :

from sqlalchemy.sql import case

query(MyTable).filter(
    MyTable.col1.in_(payload)
).update({
    MyTable.col2: case(
        payload,
        value=MyTable.col1,
    )
}, synchronize_session=False)

Выше a) выбирает строки, в которых значение col1 является ключом в словаре payload, затем b) обновляетcol2 значение столбца с помощью оператора CASE, который выбирает значения из того же словаря для обновления этого столбца на основе сопоставления col1 с ключами.

С payload, установленным на {'x': 'y', 'a': 'b', 'c': 'd'}, вышевыполняет следующий запрос (дает или принимает точный порядок WHEN предложений и значений в тесте IN):

UPDATE mytable
SET
    col2=CASE mytable.col1
        WHEN 'x' THEN 'y'
        WHEN 'a' THEN 'b'
        WHEN 'c' THEN 'd'
    END
WHERE
    mytable.col1 IN ('x', 'a', 'c')

Я установил synchronize_session в False, так как обновляю все возможныеКэшированные MyTable экземпляры одновременно, возможно, не лучшая идея при обновлении большого количества строк.Другие ваши варианты: 'evaluate' и 'fetch'.

  • Мы не можем использовать значение по умолчанию 'evaluate' (которое будет находить существующие в сеансе объекты, которые соответствуют предложению whereдля обновления на месте), потому что SQLAlchemy в настоящее время не знает, как обрабатывать фильтр IN (вы получаете исключение UnevaluatableError).

  • Если вы используете 'fetch' тогда все экземпляры MyTable, кэшированные в сеансе, которые были затронуты, обновляются новыми значениями для col2 (в соответствии с их первичным ключом).

Обратите внимание, что фиксацияистекает сеанс в любом случае , поэтому вы можете использовать 'fetch' только в том случае, если вам нужно проделать дополнительную работу с обновленными строками, прежде чем вы сможете зафиксировать текущую транзакцию.

См.Query.update() документация для получения дополнительной информации о том, какие опции synchronize_session у вас есть.

...