2017-10-18 31 views
2

Je tente d'utiliser un CTE avec un DELETE dans SQLAlchemy (core). Jusqu'à présent, mes tentatives ont échoué, le CTE n'étant pas inclus dans l'instruction SQL compilée. En fin de compte, cela sera exécuté sur une base de données PostgreSQL, qui supporte ce type d'instruction.SQLAlchemy n'inclut pas de CTE dans DELETE

code Python d'un exemple artificiel:

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())) 

Ce que je me attends est (ou quelque chose comme):

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

Ce que je veux est:

DELETE FROM foo WHERE foo.name = ctetbl.name 

Qu'est-ce qui me manque ici?

Répondre

1

Dans

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

vous ne l'avez pas fait inclus le CTE comme une table source dans l'instruction DELETE. Quelque chose de semblable se produit dans votre instruction SQLAlchemy en raison de no support for USING clause et qui rejette le compilateur. Vous pourriez 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 

puis

delete_stmt = tbl.delete().where(tbl.c.name == mycte.c.name) 

compilera comme

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 

vous en utilisant l'aide pourrait faire une auto-jointure ainsi:

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

Bien sûr, vous auriez pu également référencé le CTE avec un sous-select scalaire sans avoir l'extension du compilateur en place:

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

mais où est le plaisir dans tout cela ...

+0

question de Raise dans la traqueur? –

+0

Le premier lien est en fait le (très vieux) problème ouvert à ce sujet. –

+0

En ce qui concerne le sous-select scalaire, cela a du sens. Dans mon cas, je choisis plusieurs tables, donc je suppose que mon exemple ne correspond pas exactement à ce que je suis en train de travailler, mais l'extension du compilateur est un pas dans la bonne direction. Content de savoir que c'est une fonctionnalité manquante de SQLAlchemy. J'étais sûr que je n'avais pas manqué quelque chose dans la documentation. Merci beaucoup! – linux2647