【问题标题】:How to see the real SQL query in Python cursor.execute using pyodbc and MS-Access如何使用 pyodbc 和 MS-Access 在 Python cursor.execute 中查看真正的 SQL 查询
【发布时间】:2011-07-13 02:13:21
【问题描述】:

我在 Python 中使用以下代码(使用 pyodbc 作为 MS-Access 基础)。

cursor.execute("select a from tbl where b=? and c=?", (x, y))

没关系,但出于维护目的,我需要知道发送到数据库的完整且准确的 SQL 字符串。
有可能吗?如何实现?

【问题讨论】:

    标签: python sql ms-access pyodbc


    【解决方案1】:

    因驱动程序而异。这里有两个例子:

    import MySQLdb
    mc = MySQLdb.connect()
    r = mc.cursor()
    r.execute('select %s, %s', ("foo", 2))
    r._executed
    "select 'foo', 2"
    
    import psycopg2
    pc = psycopg2.connect()
    r = pc.cursor()
    r.execute('select %s, %s', ('foo', 2))
    r.query
    "select E'foo', 2"
    

    【讨论】:

    • 在 psycopg2 中还有游标的 mogrify() 方法,它可以让您准确地查看给定查询将在不执行(或之前)执行的情况下执行的命令。
    • 在 MySQLdb 中 '_last_executed' 保存最后一个要运行的查询字符串,即使发生异常也是如此。如果错误,属性 _executed 为 None。 [stackoverflow.com/a/7190914/653372]
    • 该操作专门说用于 MS Access 的 pyodbc。不是 MySQL 或 Postgres
    【解决方案2】:

    答案是:不。 我在项目的主页 Google 代码(以及 Google 组)上发布了我的问题,答案是:

    l...@deller.id.au 对问题 163 的评论 #1:cursor.mogrify 返回查询字符串 http://code.google.com/p/pyodbc/issues/detail?id=163

    这里有一个链接供参考 他们的 pyscopg 文档 “mogrify”游标方法 记者指的是: http://initd.org/psycopg/docs/cursor.html#cursor.mogrify

    pyodbc 不执行任何此类操作 SQL的翻译:它通过 参数化 SQL 直通到 ODBC 驱动程序逐字记录。唯一的 涉及的处理是翻译 从 Python 对象到 C 的参数 ODBC API 支持的类型。

    SQL 上的一些转换可能是 在它之前的 ODBC 驱动程序中执行 被发送到服务器(例如 Microsoft SQL Native Client 执行此操作)但这些 转换是隐藏的 pyodbc。

    因此我认为不是 提供一个 mogrify 功能是可行的 在pyodbc中。

    【讨论】:

    • 这当然是可能的。它只是意味着重新实现由 ODBC 驱动程序执行的转换。通常这只是一些逃避。
    【解决方案3】:

    您可以使用print cursor._last_executed 获取最后执行的查询。

    阅读this 的答案,您也可以使用print cursor.mogrify(query,list) 在执行之前或之后查看完整的查询。

    【讨论】:

    • cursor.mogrify(query,list) 也适用于 python3 和 psycopg2
    【解决方案4】:

    为了调试目的,我创建了一个检查函数,它只是替换 ?使用查询值......这不是高科技:)但它有效! :D

    def check_sql_string(sql, values):
        unique = "%PARAMETER%"
        sql = sql.replace("?", unique)
        for v in values: sql = sql.replace(unique, repr(v), 1)
        return sql
    
    query="""SELECT * FROM dbo.MA_ItemsMonthlyBalances
                       WHERE Item = ? AND Storage = ? AND FiscalYear = ? AND BalanceYear = ? AND Balance = ? AND BalanceMonth = ?"""
    values = (1,2,"asdasd",12331, "aas)",1)
    
    print(check_sql_string(query,values))
    

    结果:

    SELECT * FROM dbo.MA_ItemsMonthlyBalances WHERE Item = 1 AND St​​orage = 2 AND FiscalYear = 'asdasd' AND BalanceYear = 12331 AND Balance = 'aas') AND BalanceMonth = 1

    有了这个,你可以登录或做任何你想做的事:

    rowcount = self.cur.execute(query,values).rowcount
    logger.info(check_sql_string(query,values))
    

    如果您需要在函数中添加一些异常捕获。

    【讨论】:

      【解决方案5】:

      根据您使用的驱动程序,这可能会也可能不会。在某些数据库中,参数 (?s) 被简单地替换,正如 user589983 的回答所暗示的那样(尽管驱动程序必须做一些事情,比如在这些字符串中引用字符串和转义引号,以便生成一个可执行的语句) .

      其他驱动程序会要求数据库编译(“准备”)语句,然后要求它使用给定的值执行准备好的语句。正是通过这种方式,使用准备好的或参数化的语句有助于避免 SQL 注入——在语句执行时,数据库“知道”您希望运行的 SQL 的一部分,以及其中使用的值的一部分那句话。

      PyODBC documentation 的快速浏览来看,似乎不可能执行实际的 SQL,但我可能错了。

      【讨论】:

        【解决方案6】:

        我会在之后检查 cursor._last_executed,但如果你希望它们实时打印出来而不改变每次执行,试试这个猴子补丁:

        def log_queries(cur):
            def _query(q):
                print q # could also use logging
                return cur._do_query(q)
            cur._query = _query
        
        conn = MySQLdb.connect( read_default_file='~/.my.cnf' )
        cur = conn.cursor()
        log_queries(cur)
        cur.execute('SELECT %s, %s, %s', ('hello','there','world'))
        

        它非常依赖 MySQLdb(并且可能会在以后的版本中中断)。它之所以有效,是因为 cur._query 当前只是调用 calls._do_query 并返回其结果。

        【讨论】:

          【解决方案7】:

          我使用Wireshark 在pyodbc 中查看实际的SQL 字符串。如果您使用不受保护的服务器连接进行开发,这可能会有所帮助。

          【讨论】:

          • 此方法不适用于针对本机 Access 表的查询。此类查询由 Access 数据库引擎处理,您将在 Wireshark 中看到来自数据库文件的页面的 SMB/CIFS 请求(和响应),这比正在处理的 SQL 语句级别低得多。您可以看到针对 ODBC 链接表的查询的 SQL 文本,但它将是带有参数 placeholders(例如,SELECT * FROM tablename WHERE columnname > @P1)的原始 SQL 文本,而不是带有参数 values 插入。
          【解决方案8】:

          由于 pyodbc 无法在执行查询之前查看查询。您可以手动预填充查询,以了解最终的外观。它不会像实际查询那样工作,但它帮助我弄清楚在需要超过 40 个参数的查询中是否有任何错误。

          query = """select * from [table_name] where a = ? and b = ?"""
          
          parameter_list = ['dog', 'cat'] # List of parameters, a = 'dog', b = 'cat'.
          
          query_list = list(query) # Split query string into individual characters.
          
          # Loop through list and populate the question marks.
          for i in range(len(parameter_list)):
              for idx, val in enumerate(query_list):
                  if val == '?':
                      query_list[idx] = str(parameter_list[i])
                      break
          
          # Rejoin the query string.
          query_populate = ''.join(query_list)
          
          #### Result ####
          """select * from [table_name] where a = dog and b = cat"""
          

          【讨论】:

            【解决方案9】:

            编写sql字符串然后执行:

            sql='''select a 
                   from tbl 
                   where b=? 
                   and c=? '''
            
            cursor.execute(sql, x, y)
            print 'just executed:',(sql, x,y)
            

            现在您可以使用 SQL 语句做任何您想做的事情。

            【讨论】:

            • 对我也不好:我在“执行”中传递变量是出于不同的原因,SQL 注入也是因为“执行”函数根据数据库类型和列类型修改 SQL 查询.示例:我可以传递字符串或整数或日期字段,而不必担心在查询中引用它们。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-12-05
            • 2014-08-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多