我正在尝试在SQLAlchemy(核心)中使用带DELETE
的CTE。到目前为止,我的尝试都没有成功,因为CTE没有包含在已编译的SQL语句中。最终,这将针对PostgreSQL数据库运行,该数据库支持这种语句。
一个人为举例的Python代码:
from sqlalchemy import *
from sqlalchemy.dialects import postgresql
metadata = MetaData()
tbl = Table('foo', metadata,
Column('id', Integer, primary_key = True),
Column('name', String)
)
mycte = select([tbl.c.name]).where(tbl.c.id == 123).cte('ctetbl')
delete_stmt = tbl.delete().where(tbl.c.name == mycte.c.name)
print("Regular:", delete_stmt.compile())
print("Postgres:", delete_stmt.compile(dialect = postgresql.dialect()))
我期待的是(或类似的东西):
WITH ctetbl AS (
SELECT name FROM foo
WHERE id = 123
)
DELETE FROM foo WHERE foo.name = ctetbl.name
我得到的是:
DELETE FROM foo WHERE foo.name = ctetbl.name
我在这里缺少什么?
答案 0 :(得分:1)
在
WITH ctetbl AS (
SELECT name FROM foo
WHERE id = 123
)
DELETE FROM foo WHERE foo.name = ctetbl.name
您实际上并未将CTE作为DELETE语句中的源表包含在内。由于no support for USING clause,SQLAlchemy语句中发生了类似的事情并且会抛出编译器。你可以add support for it with a compiler extension:
from sqlalchemy import *
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.sql.expression import Delete, Update
import re
@compiles(Delete, 'postgresql')
def compile_delete(element, compiler, **kw):
using_clause = None
extra_froms = Update._extra_froms.__get__(element)
if extra_froms:
# Pre-compile extra_froms in order to populate CTEs before
# compiling the delete statement itself
using_clause = "USING %s" % ', '.join(
compiler.process(fr, asfrom=True, **kw)
for fr in extra_froms
)
text = compiler.visit_delete(element, **kw)
if using_clause:
# NOTE: This will blow up badly, if your CTEs also
# contain DELETE statements.
text = re.sub(
r"(DELETE FROM \S+)",
lambda m: "%s %s" % (m.group(1), using_clause),
text
)
return text
然后
delete_stmt = tbl.delete().where(tbl.c.name == mycte.c.name)
将编译为
WITH ctetbl AS
(SELECT foo.name AS name
FROM foo
WHERE foo.id = %(id_1)s)
DELETE FROM foo USING ctetbl WHERE foo.name = ctetbl.name
使用USING你也可以进行自我加入:
...: tbl_alias = tbl.alias()
...: delete_stmt = tbl.delete().\
...: where(tbl.c.name == tbl_alias.c.name).\
...: where(tbl_alias.c.id == 123)
...:
...: print("Postgres:", delete_stmt.compile(dialect = postgresql.dialect()))
...:
Postgres: DELETE FROM foo USING foo AS foo_1 WHERE foo.name = foo_1.name AND foo_1.id = %(id_1)s
当然,您也可以使用标量子选择引用CTE,而无需编译器扩展:
...: delete_stmt = tbl.delete().where(tbl.c.name == mycte.select().as_scalar())
...:
...: print("Postgres:", delete_stmt.compile(dialect = postgresql.dialect()))
Postgres: WITH ctetbl AS
(SELECT foo.name AS name
FROM foo
WHERE foo.id = %(id_1)s)
DELETE FROM foo WHERE foo.name = (SELECT ctetbl.name
FROM ctetbl)
但那里的乐趣......