Как увидеть настоящий SQL-запрос в Python cursor.execute с помощью pyodbc и MS-Access

Я использую следующий код в Python (с pyodbc для базы MS-Access).

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

Это нормально, но в целях обслуживания мне нужно знать полную и точную строку SQL, отправляемую в базу данных.
Возможно ли это и как?


person philnext    schedule 10.03.2011    source источник


Ответы (9)


Отличается драйвером. Вот два примера:

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"
person samplebias    schedule 10.03.2011
comment
в psycopg2 также есть метод курсора mogrify(), который позволяет вам точно увидеть, какая команда будет выполнена данным запросом без (или до) его выполнения. - person kindall; 11.03.2011
comment
В MySQLdb '_last_executed' содержит последнюю строку запроса для выполнения, даже если возникает исключение. Свойство _executed имеет значение None в случае ошибки. [stackoverflow.com/a/7190914/653372] - person nergeia; 03.04.2013
comment
В операторе конкретно указано pyodbc для MS Access. Не MySQL или Postgres - person ThatAintWorking; 14.09.2016

Ответ - нет. Я разместил свой вопрос в домашнем коде Google проекта (и в группе Google), и ответ таков:

Комментарий № 1 к проблеме 163 от [email protected]: 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, поддерживаемые API ODBC.

Некоторое преобразование SQL может быть выполнено в драйвере ODBC перед его отправкой на сервер (например, это делает собственный клиент Microsoft SQL), но эти преобразования скрыты от pyodbc.

Следовательно, я думаю, что невозможно предоставить функцию mogrify в pyodbc.

person philnext    schedule 16.03.2011
comment
Это, конечно, возможно. Это просто означает повторную реализацию преобразований, выполняемых драйвером ODBC. Обычно это просто побег. - person Michael Mior; 26.10.2012

Вы можете использовать print cursor._last_executed для получения последнего выполненного запроса.

Прочтите в этом ответе, что вы также можете использовать print cursor.mogrify(query,list) для просмотра полного запроса до или после выполнения.

person joseph    schedule 24.02.2017
comment
cursor.mogrify(query,list) также работает с python3 и psycopg2. - person Romain; 30.05.2018

Для отладки я создал функцию проверки, которая просто заменяет ? со значениями запроса... это не высокие технологии :) но работает! :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, ГДЕ Item = 1 AND Storage = 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))

Если вам нужно просто добавить в функцию перехват исключений.

person piertoni    schedule 27.02.2014

В зависимости от используемого драйвера это может быть или не быть возможным. В некоторых базах данных параметры (?s) просто заменяются, как предполагает ответ пользователя 589983 (хотя драйверу придется делать некоторые вещи, такие как цитирование строк и экранирование кавычек в этих строках, чтобы получить исполняемый оператор).

Другие драйверы будут просить базу данных скомпилировать («подготовить») оператор, а затем попросить его выполнить подготовленный оператор, используя заданные значения. Таким образом, использование подготовленных или параметризованных операторов помогает избежать SQL-инъекций — во время выполнения оператора база данных «знает», какую часть SQL вы хотите запустить, и какая часть значения используется в нем. это заявление.

Судя по беглому просмотру документации PyODBC, не похоже, что получение фактически выполненный SQL возможен, но я могу ошибаться.

person dcrosta    schedule 10.03.2011

После этого я бы проверил 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 в настоящее время просто вызывает call._do_query и возвращает результат.

person steveayre    schedule 02.12.2016

Я использовал Wireshark, чтобы увидеть фактическую строку SQL в pyodbc. Это может помочь, если вы используете незащищенное подключение к серверу для разработки.

person user2924768    schedule 22.10.2019
comment
Этот подход не будет работать для запроса к собственной таблице Access. Такие запросы обрабатываются механизмом базы данных Access, и то, что вы увидите в Wireshark, будет запросами (и ответами) SMB/CIFS для страниц из файла базы данных, что является чем-то гораздо более низким уровнем, чем обрабатываемый оператор SQL. Вы можете увидеть текст SQL для запроса к связанной таблице ODBC, но это будет необработанный текст SQL с параметром заполнители (например, SELECT * FROM tablename WHERE columnname > @P1), а не с параметром значения вставил. - person Gord Thompson; 12.06.2020

Поскольку у 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"""
person Community    schedule 13.03.2020

Напишите строку sql, а затем выполните ее:

sql='''select a 
       from tbl 
       where b=? 
       and c=? '''

cursor.execute(sql, x, y)
print 'just executed:',(sql, x,y)

Теперь вы можете делать с оператором SQL все, что хотите.

person garnertb    schedule 10.03.2011
comment
Нехорошо и для меня: я передаю переменные в «выполнить» по разным причинам, из-за инъекции SQL, а также потому, что функция «выполнить» изменяет запрос SQL в соответствии с типом базы данных и типом столбцов. Пример: я могу передать строку, целое число или поле даты, не беспокоясь об их цитировании в запросе. - person philnext; 11.03.2011