时间:2011-01-06 16:43:06

标签: python sql mysql sqlalchemy

12 个答案:

答案 0 :(得分:76)

This博客提供了更新的答案。

从博客文章引用,这是建议并为我工作。

>>> from sqlalchemy.dialects import postgresql
>>> print str(q.statement.compile(dialect=postgresql.dialect()))

其中q定义为:

>>> q = DBSession.query(model.Name).distinct(model.Name.value) \
             .order_by(model.Name.value)

或者任何类型的session.query()。

感谢Nicolas Cadou的回答!我希望它能帮助那些来这里寻找的人。

答案 1 :(得分:56)

documentation使用literal_binds打印包含参数的查询q

print(q.statement.compile(compile_kwargs={"literal_binds": True}))
  

上述方法有一些警告,它只支持基本类型,如int和字符串,而且如果直接使用没有预设值的bindparam(),它将无法将其字符串化任

答案 2 :(得分:24)

答案 3 :(得分:17)

答案 4 :(得分:13)

答案 5 :(得分:8)

对于使用psycopg2的postgresql后端,您可以侦听do_execute事件,然后使用游标,语句和类型强制参数以及Cursor.mogrify()来内联参数。您可以返回True以防止实际执行查询。

import sqlalchemy

class QueryDebugger(object):
    def __init__(self, engine, query):
        with engine.connect() as connection:
            try:
                sqlalchemy.event.listen(engine, "do_execute", self.receive_do_execute)
                connection.execute(query)
            finally:
                sqlalchemy.event.remove(engine, "do_execute", self.receive_do_execute)

    def receive_do_execute(self, cursor, statement, parameters, context):
        self.statement = statement
        self.parameters = parameters
        self.query = cursor.mogrify(statement, parameters)
        # Don't actually execute
        return True

样本用法:

>>> engine = sqlalchemy.create_engine("postgresql://postgres@localhost/test")
>>> metadata = sqlalchemy.MetaData()
>>> users = sqlalchemy.Table('users', metadata, sqlalchemy.Column("_id", sqlalchemy.String, primary_key=True), sqlalchemy.Column("document", sqlalchemy.dialects.postgresql.JSONB))
>>> s = sqlalchemy.select([users.c.document.label("foobar")]).where(users.c.document.contains({"profile": {"iid": "something"}}))
>>> q = QueryDebugger(engine, s)
>>> q.query
'SELECT users.document AS foobar \nFROM users \nWHERE users.document @> \'{"profile": {"iid": "something"}}\''
>>> q.statement
'SELECT users.document AS foobar \nFROM users \nWHERE users.document @> %(document_1)s'
>>> q.parameters
{'document_1': '{"profile": {"iid": "something"}}'}

答案 6 :(得分:3)

以下解决方案使用SQLAlchemy Expression Language并使用SQLAlchemy 1.1。此解决方案不会将参数与查询混合(根据原作者的要求),但提供了一种使用SQLAlchemy模型为不同SQL方言生成SQL查询字符串和参数字典的方法。该示例基于教程http://docs.sqlalchemy.org/en/rel_1_0/core/tutorial.html

鉴于课程,

from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class foo(Base):
    __tablename__ = 'foo'
    id = Column(Integer(), primary_key=True)
    name = Column(String(80), unique=True)
    value = Column(Integer())

我们可以使用选择函数生成查询语句。

from sqlalchemy.sql import select    
statement = select([foo.name, foo.value]).where(foo.value > 0)

接下来,我们可以将语句编译成查询对象。

query = statement.compile()

默认情况下,语句使用基本的'命名'进行编译。与SQLite和Oracle等SQL数据库兼容的实现。如果你需要指定一个方言,如PostgreSQL,你可以做

from sqlalchemy.dialects import postgresql
query = statement.compile(dialect=postgresql.dialect())

或者,如果您想将方言显式指定为SQLite,则可以从' qmark'中更改paramstyle。以'命名'。

from sqlalchemy.dialects import sqlite
query = statement.compile(dialect=sqlite.dialect(paramstyle="named"))

从查询对象中,我们可以提取查询字符串和查询参数

query_str = str(query)
query_params = query.params

最后执行查询。

conn.execute( query_str, query_params )

答案 7 :(得分:3)

首先让我先说一下,我假设您主要是出于调试目的而这样做 - 我不建议尝试修改SQLAlchemy流利API之外的语句。

不幸的是,似乎并不是一种简单的方法来显示包含查询参数的编译语句。 SQLAlchemy实际上并没有将参数放入语句中 - 它们是passed into the database engine as a dictionary。这使得特定于数据库的库可以处理诸如转义特殊字符之类的事情,以避免SQL注入。

但是你可以很容易地分两步完成这个过程。要获得声明,您可以按照您已经显示的那样进行操作,然后打印查询:

>>> print(query)
SELECT field_1, field_2 FROM table WHERE id=%s;

您可以使用query.statement更近一步,查看参数名称。 (注意下面的:id_1与上面的%s相比 - 在这个非常简单的示例中并不是真正的问题,但在更复杂的陈述中可能是关键。)

>>> print(query.statement)
>>> print(query.statement.compile()) # reasonably equivalent, you can also
                                     # pass in a dialect if you want
SELECT field_1, field_2 FROM table WHERE id=:id_1;

然后,您可以通过获取已编译语句的params属性来获取参数的实际值:

>>> print(query.statement.compile().params)
{u'id_1': 1} 

这至少适用于MySQL后端;我希望它对PostgreSQL也足够通用,而不需要使用psycopg2

答案 8 :(得分:1)

您可以使用ConnectionEvents系列中的活动:after_cursor_executebefore_cursor_execute

在@zzzeek的sqlalchemy UsageRecipes中,你可以找到这个例子:

Profiling

...
@event.listens_for(Engine, "before_cursor_execute")
def before_cursor_execute(conn, cursor, statement,
                        parameters, context, executemany):
    conn.info.setdefault('query_start_time', []).append(time.time())
    logger.debug("Start Query: %s" % statement % parameters)
...

您可以在此处访问声明

答案 9 :(得分:0)

我认为.statement可能会做到这一点: http://docs.sqlalchemy.org/en/latest/orm/query.html?highlight=query

>>> local_session.query(sqlalchemy_declarative.SomeTable.text).statement
<sqlalchemy.sql.annotation.AnnotatedSelect at 0x6c75a20; AnnotatedSelectobject>
>>> x=local_session.query(sqlalchemy_declarative.SomeTable.text).statement
>>> print(x)
SELECT sometable.text 
FROM sometable

答案 10 :(得分:0)

因此,将这些不同的答案汇总在一起,得出了我需要的内容:一组简单的代码可以插入,偶尔但可靠地(即处理所有数据类型)获取准确的,已编译的SQL通过查询查询本身发送到我的Postgres后端:

from sqlalchemy.dialects import postgresql

query = [ .... some ORM query .... ]

compiled_query = query.statement.compile(
    dialect=postgresql.dialect()
)
mogrified_query = session.connection().connection.cursor().mogrify(
    str(compiled_query),
    compiled_query.params
)

print("compiled SQL = {s}".format(mogrified_query.decode())

答案 11 :(得分:0)

我已经创建了这个小功能,当我想打印完整查询时会导入该功能,考虑到当方言已经绑定时我正处于测试中间:

import re

def print_query(query):
    regex = re.compile(":(?P<name>\w+)")
    params = query.statement.compile().params
    sql = regex.sub("'{\g<name>}'", str(query.statement)).format(**params)
    print(f"\nPrinting SQLAlchemy query:\n\n")
    print(sql)
    return sql