我正在开始一个新的应用程序,并着眼于使用ORM - 特别是SQLAlchemy。
假设我的数据库中有一个列'foo',我想增加它。在直接的sqlite中,这很容易:
db = sqlite3.connect('mydata.sqlitedb')
cur = db.cursor()
cur.execute('update table stuff set foo = foo + 1')
我想出了SQLAlchemy SQL-builder的等价物:
engine = sqlalchemy.create_engine('sqlite:///mydata.sqlitedb')
md = sqlalchemy.MetaData(engine)
table = sqlalchemy.Table('stuff', md, autoload=True)
upd = table.update(values={table.c.foo:table.c.foo+1})
engine.execute(upd)
这稍微慢一点,但其中并不多。
这是我对SQLAlchemy ORM方法的最佳猜测:
# snip definition of Stuff class made using declarative_base
# snip creation of session object
for c in session.query(Stuff):
c.foo = c.foo + 1
session.flush()
session.commit()
这是正确的,但它只需要不到其他两种方法的五十倍。我认为这是因为它必须将所有数据带入内存才能使用它。
有没有办法使用SQLAlchemy的ORM生成高效的SQL?或者使用任何其他python ORM?或者我应该回去手工编写SQL?
答案 0 :(得分:152)
SQLAlchemy的ORM旨在与SQL层一起使用,而不是隐藏它。但是,在同一事务中使用ORM和纯SQL时,您必须记住一两件事。基本上,从一方面来看,ORM数据修改只会在您从会话中刷新更改时命中数据库。另一方面,SQL数据操作语句不会影响会话中的对象。
所以,如果你说
for c in session.query(Stuff).all():
c.foo = c.foo+1
session.commit()
它将执行它所说的内容,从数据库中获取所有对象,修改所有对象,然后在将更改刷新到数据库时,逐个更新行。
相反,你应该这样做:
session.execute(update(stuff_table, values={stuff_table.c.foo: stuff_table.c.foo + 1}))
session.commit()
这将按照您的预期执行一个查询,并且由于至少默认会话配置会在提交时过期会话中的所有数据,因此您没有任何陈旧的数据问题。
在几乎发布的0.5系列中,您还可以使用此方法进行更新:
session.query(Stuff).update({Stuff.foo: Stuff.foo + 1})
session.commit()
这基本上会运行与前一个代码段相同的SQL语句,但也会选择更改的行并使会话中的任何陈旧数据失效。如果您知道在更新后没有使用任何会话数据,您还可以将updateize_session = False添加到更新语句并删除该选择。
答案 1 :(得分:78)
session.query(Clients).filter(Clients.id == client_id_list).update({'status': status})
session.commit()
试试这个=)
答案 2 :(得分:18)
有几种方法可以使用sqlalchemy
进行更新1) for c in session.query(Stuff).all():
c.foo += 1
session.commit()
2) session.query().\
update({"foo": (Stuff.foo + 1)})
session.commit()
3) conn = engine.connect()
stmt = Stuff.update().\
values(Stuff.foo = (Stuff.foo + 1))
conn.execute(stmt)
答案 3 :(得分:1)
以下是如何在不必手动映射字段的情况下解决同一问题的示例:
newOrderService.getNewOrders().then(function (response) {
if (timer === null) {
timer = $interval(function(){
$scope.checkNewOrders();
}.bind(this), 10000);
}
...
因此,要更新Media实例,您可以执行以下操作:
from sqlalchemy import Column, ForeignKey, Integer, String, Date, DateTime, text, create_engine
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.attributes import InstrumentedAttribute
engine = create_engine('postgres://postgres@localhost:5432/database')
session = sessionmaker()
session.configure(bind=engine)
Base = declarative_base()
class Media(Base):
__tablename__ = 'media'
id = Column(Integer, primary_key=True)
title = Column(String, nullable=False)
slug = Column(String, nullable=False)
type = Column(String, nullable=False)
def update(self):
s = session()
mapped_values = {}
for item in Media.__dict__.iteritems():
field_name = item[0]
field_type = item[1]
is_column = isinstance(field_type, InstrumentedAttribute)
if is_column:
mapped_values[field_name] = getattr(self, field_name)
s.query(Media).filter(Media.id == self.id).update(mapped_values)
s.commit()
答案 4 :(得分:0)
经过充分测试,我会尝试:
for c in session.query(Stuff).all():
c.foo = c.foo+1
session.commit()
(IIRC,commit()没有flush())。
我发现有时进行大型查询然后在python中迭代可以比大量查询快2个数量级。我假设迭代查询对象的效率低于迭代查询对象的all()方法生成的列表。
[请注意下面的评论 - 这根本没有加快速度]。
答案 5 :(得分:0)
如果是因为创建对象的开销,那么可能无法用SA加速。
如果是因为它正在加载相关对象,那么您可以使用延迟加载执行某些操作。是否因为引用而创建了大量对象? (IE,获取Company对象也会获取所有相关的People对象)。