SQLAlchemy: 在MySQL上使用自连接创建删除查询

1 投票
2 回答
3149 浏览
提问于 2025-04-17 16:45

我正在尝试在SQLAlchemy和MySQL中复制以下查询,但一直没有成功……

delete paths from paths
join paths as p1 on p1.ancestor = <ID>
where paths.descendant = p1.descendant;

SQLAlchemy似乎忽略了我在删除查询中添加的任何连接。我还尝试过使用子查询来代替连接,但在MySQL中这是不允许的(不能从你想要删除的同一张表中选择)。任何帮助都将非常感激。

更新:是的,我正在尝试使用ORM层。以下是我尝试过的查询:

p1 = aliased(Path, name="p1")
db.session.query(Path).join(
    p1, p1.ancestor==<ID>
)
.filter(
    Path.descendant==p1.Path.descendant
).delete()

还有子查询的变体,但在MySQL中这不起作用,所以对我没有用:

q = db.session.query(Path.descendant).filter(Path.ancestor==<ID>).subquery()
db.session.query(Path).filter(Path.descendant.in_(q)).delete(synchronize_session='fetch')

2 个回答

2

你可以使用 prefixes 这个参数:

j = join(table1, table2, table1.c.key==table2.c.key)
stmt = delete(j, prefixes=[table1_name])
session.execute(stmt)

prefixes 这个参数的作用是让你在一些特定的操作,比如 SELECT、INSERT、UPDATE 或 DELETE 语句后面,添加一个或多个表达式。

在这个例子中,delete(j) 这个语句生成的表达式是:"DELETE FROM table1 INNER JOIN table2 ON table1.key=table2.key"。当我们加上 prefixes 参数后,表达式变成了:"DELETE table1 FROM table1 INNER JOIN table2 ON table1.key=table2.key",这样就形成了一个正确的 MySQL 查询。

2

SQLAlchemy 现在支持在 Postgresql、MySQL 等数据库中使用 UPDATE..FROM 语句,但目前还没有尝试支持 DELETE..JOIN 语句。

不过,生成 SQL 字符串的时候,它似乎是可以工作的(几乎可以?):

class Path(Base):
    __tablename__ = "path"

    id = Column(Integer, primary_key=True)
    descendant = Column(Integer)
    ancestor = Column(Integer)

j = join(Path, p1, p1.ancestor == 5)
d = delete(j).where(Path.descendant == p1.descendant)
print d

打印结果是:

DELETE FROM path JOIN path AS p1 ON p1.ancestor = :ancestor_1 
 WHERE path.descendant = p1.descendant

但是,我的 MySQL 数据库默认不接受这个,它会生成 INNER JOIN,这样就失败了。不过如果我修改 MySQL 的编译器不这样做,还是会失败:

s.execute(d)

(ProgrammingError) (1064, "You have an error in your SQL syntax; check the manual that 
corresponds to your MySQL server version for the
right syntax to use near 'JOIN path AS p1 ON p1.ancestor = 5 WHERE
path.descendant = p1.descendant' at line 1") 'DELETE FROM path JOIN
path AS p1 ON p1.ancestor = %s WHERE path.descendant = p1.descendant'
(5,)

看起来你的 SQL 和原始的一样(哦,除了 'delete paths FROM paths' 这个部分?对吗?)

无论如何,如果内置的编译器不支持这个,你可以选择使用 session.execute("some sql"),或者用 编译器扩展来构建一个自定义的结构。

撰写回答