使用SQLAlchemy删除没有相关记录的行

时间:2013-07-15 02:16:27

标签: python postgresql python-2.7 sqlalchemy

我有2张桌子;我们称他们为table1table2table2table1的外键。我需要在table1中删除table2中没有子记录的行。执行此操作的SQL非常简单:

DELETE FROM table1
WHERE 0 = (SELECT COUNT(*) FROM table2 WHERE table2.table1_id = table1.table1_id);

但是,我还没有找到将此查询转换为SQLAlchemy的方法。尝试直截了当的方法:

subquery = session.query(sqlfunc.count(Table2).label('t2_count')).select_from(Table2).filter(Table2.table1_id == Table1.table1_id).subquery()
session.query(Table1).filter(0 == subquery.columns.t2_count).delete()

刚出错:

sqlalchemy.exc.ArgumentError: Only deletion via a single table query is currently supported

如何使用SQLAlchemy执行此DELETE

  • Python 2.7
  • PostgreSQL 9.2.4
  • SQLAlchemy 0.7.10(由于使用GeoAlchemy而无法升级,但如果更新的版本会让这更容易,我感兴趣)

2 个答案:

答案 0 :(得分:2)

我很确定这就是你想要的。你应该尝试一下。它使用EXISTS。

from sqlalchemy.sql import not_

# This fetches rows in python to determine which ones were removed.
Session.query(Table1).filter(not_(Table1.table2s.any())).delete(
    synchronize_session='fetch')

# If you will not be referencing more Table1 objects in this session then you
# can just ignore syncing the session.
Session.query(Table1).filter(not_(Table1.table2s.any())).delete(
    synchronize_session=False)

delete()的参数说明:

http://docs.sqlalchemy.org/en/rel_0_8/orm/query.html#sqlalchemy.orm.query.Query.delete

存在的示例(使用上面的any()使用EXISTS):

http://docs.sqlalchemy.org/en/rel_0_8/orm/tutorial.html#using-exists

以下是应该生成的SQL:

DELETE FROM table1 WHERE NOT (EXISTS (SELECT 1 
FROM table2 
WHERE table1.id = table2.table1_id))

如果你使用声明,我认为有一种方法可以访问Table2。然后你可以使用sqlalchemy的sql层来完全按照自己的意愿行事。虽然您遇到了使会话不同步的相同问题。

答案 1 :(得分:1)

好吧,我找到了一个非常难看的方法。您可以使用连接进行选择以将行加载到内存中,然后您可以单独删除它们:

subquery = session.query(Table2.table1_id
                        ,sqlalchemy.func.count(Table2.table2_id).label('t1count')
                        ) \
                  .select_from(Table2) \
                  .group_by(Table2.table1_id) \
                  .subquery()
rows = session.query(Table1) \
              .select_from(Table1) \
              .outerjoin(subquery, Table1.table1_id == subquery.c.table1_id) \
              .filter(subquery.c.t1count == None) \
              .all()
for r in rows:
    session.delete(r)

这不仅令人讨厌,而且性能也非常糟糕。对于初学者,您必须将table1行带入内存。第二,如果你像我一样,在Table2的班级定义中有这样的一行:

table1 = orm.relationship(Table1, backref=orm.backref('table2s'))

然后SQLAlchemy将实际执行一个查询,将相关的table2行拉入内存(即使没有)。更糟糕的是,因为你必须遍历列表(我尝试只是传入列表;不起作用),它一次一行table1。因此,如果您要删除10行,则会有21个单独的查询(1表示初始选择,1表示每个关系拉,1表示每次删除)。也许有办法减轻这种情况;我必须通过文档来查看。所有这些都是我在数据库中甚至不需要的东西,更不用说内存了。

我不会将此标记为答案。我想要一种更干净,更有效的方法,但这就是我现在所拥有的一切。