2017-10-18 4 views
2

SQLAlchemy(コア)でDELETEのCTEを利用しようとしています。これまでのところ、CTEがコンパイルされたSQL文に含まれていないという私の試みは失敗しました。最終的に、これはPostgreSQLデータベースに対して実行され、この種の文をサポートします。不自然な例のSQLAlchemyがDELETEのCTEを含まない

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 

私はここで何が欠けていますか?

答えて

1

WITH ctetbl AS (
    SELECT name FROM foo 
    WHERE id = 123 
) 
DELETE FROM foo WHERE foo.name = ctetbl.name 

では、実際にDELETE文のソース表としてCTEが含まれていませんでした。何か似たようなものがno support for USING clauseのためにあなたのSQLAlchemyステートメントで起こり、それはコンパイラをスローします。あなたはadd support for it with a compiler extensionができます

...: 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 

:君を利用して使用すると、自己結合を同様に行うことができ

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 

として

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) 

をコンパイルしますもちろん210

あなたはまた、スカラー副問い合わせ代わりにコンパイラの拡張機能を有しないとの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) 

が、中に楽しいことしたいです...

+0

レイズの問題トラッカー? –

+0

最初のリンクは実際にはこれについての(非常に古い)未解決の問題です。 –

+0

スカラーサブセレクトに関しては意味があります。私自身のケースでは、複数のテーブルから選択しているので、私の例は私が扱っているものとまったく同じではないと思いますが、コンパイラの拡張は正しい方向の一歩です。これがSQLAlchemyの欠けている機能であることを知ってうれしいです。私はドキュメンテーションで何かを逃さなかったと確信していました。大変ありがとう! – linux2647

関連する問題