3

我有 2 张桌子;我们会打电话给他们table1table2table2有一个外键table1。我需要删除中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

如何DELETE使用 SQLAlchemy 执行此操作?

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

2 回答 2

3

我很确定这就是你想要的。不过你应该试试看。它使用 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 层来做你想要的。尽管您遇到了使会话不同步的相同问题。

于 2013-07-16T23:18:30.227 回答
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 个用于每次删除)。也许有办法减轻这种情况;我必须通过文档才能看到。所有这些都是为了我什至不希望在我的数据库中出现的东西,更不用说在内存中了。

我不会将此标记为答案。我想要一种更清洁、更有效的方式来做到这一点,但这就是我现在所拥有的。

于 2013-07-15T07:10:16.880 回答