考虑使用联接继承的以下SQLAlchemy映射:
from sqlalchemy import sa
class Location(Base):
id = Column(Integer, primary_key=True)
name = sa.Column(sa.String)
type_ = sa.column(sa.String)
__tablename__ = 'location'
__mapper_args__ = {
'polymorphic_identity': 'location',
'polymorphic_on': type_,
}
class Field(Location):
id = Column(Integer, primary_key=True)
size = sa.Column(sa.Float)
__tablename__ = 'field'
__mapper_args__ = {
'polymorphic_identity': 'field',
}
__table_args__ = (
sa.ForeignKeyConstraint(['id'], ['location.id']),
)
session.query(Field).filter(Field.size < 5).delete()
其中base是适当的声明性基础,session是适当的会话对象。上面的实现将导致删除Field对象而不删除父Location对象(因为文档解释清楚,query.delete()不支持继承)。我可以通过使用ORM删除链上的对象session.delete(obj)
来解决这个问题。但是,这会导致 n SQL删除语句在数据库上执行(其中 n 是要删除的对象数)。我有一个案例,我可能一次删除100,000个子对象的顺序,所以这个操作非常慢(假设我现在不可能不使用ORM加入继承 - 我太深了,无法改变这一点。)
SQLAlchemy中是否有任何构造或合理的替代方法允许我传递查询类型为Field
的对象的查询对象,并适当删除Location
表中的项目而不进行< strong> n SQL删除语句?
注意,我目前正在使用PostgreSQL,但是希望保持解决方案与数据库无关。
编辑:根据请求添加表格元数据和有关环境的更多信息。
答案 0 :(得分:0)
尝试一两个小时后,我找到了一个没有太多代码的解决方案。然后我会重现它。
1。我查看delete()
的文件。有两句话:
此方法不适用于联接的继承映射,因为SQL不支持多表删除,并且不会自动呈现继承映射器的连接条件
和
但是,上述SQL不会从Engineer表中删除,除非在数据库中建立ON DELETE CASCADE规则来处理它。
简短的故事,请不要将此方法用于联接的继承映射,除非您已采取其他步骤使其可行。
因此,定义外键约束是必要的。像这样:
class Location(Base):
__tablename__ = 'location'
id = Column(INTEGER, primary_key=True)
name = Column(VARCHAR(30))
type = Column(VARCHAR(30))
__mapper_args__ = {
'polymorphic_identity': 'location',
'polymorphic_on' : type,
}
class Field(Location):
__tablename__ = 'field'
id = Column(INTEGER, ForeignKey('location.id', ondelete='cascade'), primary_key=True)
size = Column(DECIMAL(20, 2))
__mapper_args__ = {
'polymorphic_identity': 'field',
}
2。现在,如果我们删除Location
,Field
中的行也会被删除。
session.query(Location).filter(Location.id == 1).delete()
3。但是,海报希望删除Field
而不是Location
。
session.query(Field).filter(Field.size < 5).delete()
这仅删除Field
中没有Location
行的Field
行。因为Location
是外表,所以它不能级联主表。
所以,现在我们应该根据Field.size < 5
从session.query(Location).filter(Field.size < 5).delete()
删除。
我试过了
session.query(Location).outerjoin(Field, Location.id == Field.id).filter(Field.size < 5).delete()
和
statment = delete(Field, prefixes=[Location.__tablename__]).where(Field.size == 1)
session.execute(statment)
这些都会引发异常。
经过多次尝试,我发现的解决方案是:
DELETE location FROM location JOIN field ON location.id = field.id WHERE field.size < 5
生成的sql是{{1}}