在SQLAlchemy中删除左外连接右侧的NULL
我有一个叫做 Track
的表和一个叫做 Artist
的表。 Artist
表里有一个 id
,而 Track
表里有一个外键 artist_id
。我想删除那些没有任何关联曲目的艺术家。
在 SQL 中,这样写:
delete from artists
where id in (select artists.id
from artists left outer join tracks
on tracks.artist_id = artists.id
where tracks.id is null);
这个方法运行得很好。但是当我试着在 SQLAlchemy 中复制这个操作时:
artists = session.query(Artist.id).outerjoin((Track, Artist.id == Track.artist_id)).filter(Track.id == None)
print('deleting %d unused artists' % artists.count())
session.query(Artist).filter(Artist.id.in_(artists.all())).delete()
虽然 print()
可以正常工作(并且显示正确的行数),但是删除的时候却出现了错误:
...
sqlalchemy.orm.evaluator.UnevaluatableError: Cannot evaluate clauselist with operator <function comma_op at 0x2d3e0d8>
During handling of the above exception, another exception occurred:
...
"Could not evaluate current criteria in Python. "
sqlalchemy.exc.InvalidRequestError: Could not evaluate current criteria in Python. Specify 'fetch' or False for the synchronize_session parameter.
那么我该如何在 SQLAlchemy 中做到这一点呢?我不介意方法不同,只要能删除所有没有曲目的艺术家就行。
顺便说一下,我也试过 artists.delete()
(这次是选择艺术家实例,而不是像上面那样选择 id),但也出现了错误——在这种情况下,外连接“丢失”了,导致 SQL 不一致。
更新
如果这对任何人有用的话,如果你的模型里有一个反向引用(artists
,如下所示),那么有一种比外连接简单得多的方法:
session.query(Artist).filter(Artist.tracks == None).delete(synchronize_session=False)
1 个回答
5
这个错误信息其实在告诉你怎么解决问题,你需要指定一个 synchronize_session
的值,可以是 "fetch"
或者 False
。Sqlalchemy 试图避免做一些额外的工作,它想通过直接更新与 session
关联的对象状态,来反映数据库中的变化。
不过,对于这个查询,它没办法做到这一点,所以你需要告诉它要么去“获取”当前的 Artist
对象,看看它们是否被删除,要么就选择忽略这个问题,让会话保持在一个无效的状态。
你还会在使用 in_(query.all())
时遇到一些麻烦,因为 sqlalchemy 不知道怎么处理元组的列表,而且这也不是原始查询的完美复现。其实只用 in_(query)
就足够了。