【问题标题】:Delete rows without a related record using SQLAlchemy使用 SQLAlchemy 删除没有相关记录的行
【发布时间】:2013-07-12 19:42:57
【问题描述】:

我有 2 张桌子;我们称他们为table1table2table2 有一个指向 table1 的外键。我需要删除table1table2 中子记录为零的行。执行此操作的 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 无法升级,但如果更新版本能让这更容易,我很感兴趣)

【问题讨论】:

    标签: python postgresql python-2.7 sqlalchemy


    【解决方案1】:

    我很确定这就是您想要的。不过你应该试试看。它使用 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

    exists 示例(使用上面的 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.table,然后您可以使用 sqlalchemy 的 sql 层来完成您想要的操作。尽管您遇到了使会话不同步的相同问题。

    【讨论】:

    • 谢谢!我应该自己考虑EXISTS;无论如何,它更有效,我想。效果很好。而且我实际上更喜欢在session 内进行所有操作。非常感谢。
    【解决方案2】:

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

    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 个用于每个删除)。也许有办法减轻这种情况;我必须通过文档才能看到。所有这些都是为了我的数据库中不需要的东西,更不用说内存了。

    我不会将此标记为答案。我想要一种更清洁、更有效的方法,但我目前只有这些。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-26
      • 1970-01-01
      • 2010-11-10
      • 1970-01-01
      相关资源
      最近更新 更多