如何使用字典将一列的现有值映射到另一列的所需新值,来更新数据库中的多个现有行?
我有一张桌子:
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
的 new 值;更新后,您将从数据库中获得[{'col1': 'x', 'col2': 'y'}, ...]
。
我尝试了几种方法,这些方法实际上可以工作,但是我认为它们并不像以前那样理想。
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())
)
但是我不知道如何构造更新查询。
答案 0 :(得分:2)
是的,用单个批量UPDATE
语句更新大量行比在每个对象上使用单独的UPDATE
快得多。 IN
过滤器只会帮助您限制要更新的行,但是您仍然需要告诉数据库要用于col2
更新的值。
为此,您可以使用case()
function的CASE ... WHEN ... THEN
构造:
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
子句和值的确切顺序或值): / p>
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'
。当前交易。
有关您拥有的synchronize_session
选项的更多信息,请参见Query.update()
documentation。
答案 1 :(得分:0)
我在选择的答案之前找到的另一个可行的解决方案也是:
# 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
,但会增加几秒钟的时间,但是对于发现整行并更灵活的人来说很有帮助。