Форматирование строки запроса Python SQL


102

Я пытаюсь найти лучший способ отформатировать строку запроса sql. Когда я отлаживаю свое приложение, я хочу записать в журнал все строки запроса sql, и важно, чтобы строка была правильно сформирована.

Опция 1

def myquery():
    sql = "select field1, field2, field3, field4 from table where condition1=1 and condition2=2"
    con = mymodule.get_connection()
    ...
  • Это хорошо для печати строки sql.
  • Это не лучшее решение, если строка длинная и не соответствует стандартной ширине в 80 символов.

Вариант 2

def query():
    sql = """
        select field1, field2, field3, field4
        from table
        where condition1=1
        and condition2=2"""
    con = mymodule.get_connection()
    ...
  • Здесь код ясен, но когда вы печатаете строку запроса sql, вы получаете все эти раздражающие пробелы.

    u '\ nвыбрать field1, field2, field3, field4 \ n_ _ ___ из таблицы \ n _ ___, где condition1 = 1 \ n _ ___ _and condition2 = 2'

Примечание: я заменил пробелы на подчеркивание _, потому что они обрезаны редактором.

Вариант 3

def query():
    sql = """select field1, field2, field3, field4
from table
where condition1=1
and condition2=2"""
    con = mymodule.get_connection()
    ...
  • Мне не нравится этот вариант, потому что он нарушает ясность хорошо табулированного кода.

Вариант 4

def query():
    sql = "select field1, field2, field3, field4 " \
          "from table " \
          "where condition1=1 " \
          "and condition2=2 "
    con = mymodule.get_connection()    
    ...
  • Мне не нравится этот вариант, потому что лишний набор текста в каждой строке также затруднен для редактирования запроса.

Для меня лучшим решением будет вариант 2, но мне не нравятся лишние пробелы при печати строки sql.

Вы знаете какие-нибудь другие варианты?


Это то, что люди Psycopg называют наивным подходом к составлению строк запроса, например, с использованием конкатенации строк - initd.org/psycopg/docs/… . Вместо этого используйте параметры запроса, чтобы избежать атак с использованием SQL-инъекций и для автоматического преобразования объектов Python в литералы SQL и обратно. stackoverflow.com/questions/3134691/…
Мэтью Корнелл

Этот вопрос на самом деле не относится к SQL-запросам, но обычно применяется к форматированию многострочных строк в Python. Тег SQL следует удалить.
cstork

Ответы:


136

Извините за публикацию в такой старой теме, но как человек, который также разделяет страсть к питоническим «лучшим», я подумал, что поделюсь нашим решением.

Решение состоит в том, чтобы создавать операторы SQL с использованием конкатенации строковых литералов Python ( http://docs.python.org/ ), которая может быть квалифицирована где-то между вариантом 2 и вариантом 4.

Пример кода:

sql = ("SELECT field1, field2, field3, field4 "
       "FROM table "
       "WHERE condition1=1 "
       "AND condition2=2;")

Также работает с f-струнами :

fields = "field1, field2, field3, field4"
table = "table"
conditions = "condition1=1 AND condition2=2"

sql = (f"SELECT {fields} "
       f"FROM {table} "
       f"WHERE {conditions};")

Плюсы:

  1. Он сохраняет питонический "хорошо табулированный" формат, но не добавляет лишних пробелов (что загрязняет ведение журнала).
  2. Это позволяет избежать уродливого продолжения обратной косой черты в Варианте 4, что затрудняет добавление операторов (не говоря уже о слепоте с пробелами).
  3. И, кроме того, очень просто развернуть инструкцию в VIM (просто поместите курсор в точку вставки и нажмите SHIFT-O, чтобы открыть новую строку).

2
Если это для печати, я думаю, что лучшей альтернативой будет записать его как многострочную строку """и использовать textwrap.dedent()перед
выводом

Я играл с этой опцией, но она также сделала вывод журнала многострочным. При отслеживании приложения db chatty это вызвало объемный вывод.
user590028

1
Это старый поток, но я использую этот формат как лучшую практику, однако он
утомляет

8
Разве мы не должны всегда использовать двойные кавычки, "sql query"чтобы избежать путаницы со строками SQL (которые стандартно используют одинарные кавычки)?
tpvasconcelos

19

Вы, очевидно, рассмотрели множество способов написать SQL так, чтобы он распечатывался нормально, но как насчет изменения оператора print, который вы используете для ведения журнала отладки, вместо того, чтобы писать свой SQL так, как вам не нравится? Используя ваш любимый вариант выше, как насчет такой функции ведения журнала, как эта:

def debugLogSQL(sql):
     print ' '.join([line.strip() for line in sql.splitlines()]).strip()

sql = """
    select field1, field2, field3, field4
    from table"""
if debug:
    debugLogSQL(sql)

Это также сделало бы тривиальным добавление дополнительной логики для разделения регистрируемой строки на несколько строк, если строка длиннее, чем желаемая длина.


11

Самый чистый способ, с которым я столкнулся, вдохновлен руководством по стилю sql .

sql = """
    SELECT field1, field2, field3, field4
      FROM table
     WHERE condition1 = 1
       AND condition2 = 2;
"""

По сути, ключевые слова, с которых начинается предложение, должны быть выровнены по правому краю, а имена полей и т. Д. Должны быть выровнены по левому краю. Это выглядит очень аккуратно, и его также легче отлаживать.


2
sql = ("select field1, field2, field3, field4 "
       "from table "
       "where condition1={} "
       "and condition2={}").format(1, 2)

Output: 'select field1, field2, field3, field4 from table 
         where condition1=1 and condition2=2'

если значение условия должно быть строкой, вы можете сделать следующее:

sql = ("select field1, field2, field3, field4 "
       "from table "
       "where condition1='{0}' "
       "and condition2='{1}'").format('2016-10-12', '2017-10-12')

Output: "select field1, field2, field3, field4 from table where
         condition1='2016-10-12' and condition2='2017-10-12'"

5
Пожалуйста, никогда не делай этого. Это называется SQL-инъекцией, и это действительно опасно. Практически каждая библиотека баз данных Python предоставляет возможность использования параметров. Если вы поймали себя на использовании format()строк SQL, это сильный запах кода.
mattmc3

Я не думаю, что мы не можем его использовать, вы должны проверить параметры перед его использованием, и вы должны знать, что вы передаете.
pangpang

Проверка гораздо более подвержена ошибкам, чем простое использование where condition1=:field1и последующая передача значений в качестве параметров. Если вы используете .format(), будет способ ';DROP TABLE Usersвставить в ваш SQL. Посмотрите PEP-249, чтобы узнать, как правильно использовать параметры. python.org/dev/peps/pep-0249/#paramstyle
mattmc3

1

Вы можете использовать inspect.cleandocдля красивого форматирования вашего печатного оператора SQL.

Это очень хорошо работает с вашим вариантом 2 .

Примечание: print("-"*40)это только для демонстрации лишних пустых строк, если вы не используете cleandoc.

from inspect import cleandoc
def query():
    sql = """
        select field1, field2, field3, field4
        from table
        where condition1=1
        and condition2=2
    """

    print("-"*40)
    print(sql)
    print("-"*40)
    print(cleandoc(sql))
    print("-"*40)

query()

Выход:

----------------------------------------

        select field1, field2, field3, field4
        from table
        where condition1=1
        and condition2=2

----------------------------------------
select field1, field2, field3, field4
from table
where condition1=1
and condition2=2
----------------------------------------

Из документов :

inspect.cleandoc (док)

Очистите отступы от строк документации, которые имеют отступ для выравнивания с блоками кода.

Все начальные пробелы удаляются из первой строки. Любые начальные пробелы, которые можно равномерно удалить со второй строки, удаляются. Впоследствии удаляются пустые строки в начале и конце. Кроме того, все вкладки расширены до пробелов.


0

Чтобы полностью избежать форматирования , я думаю, что отличным решением является использование процедур .

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

MYSQL

DROP PROCEDURE IF EXISTS example;
 DELIMITER //
 CREATE PROCEDURE example()
   BEGIN
   SELECT 2+222+2222+222+222+2222+2222 AS this_is_a_really_long_string_test;
   END //
 DELIMITER;

#calling the procedure gives you the result of whatever query you want to put in this procedure. You can actually process multiple queries within a procedure. The call just returns the last query result
 call example;

Python

sql =('call example;')

-1

вы можете поместить имена полей в массив "fields", а затем:


sql = 'select %s from table where condition1=1 and condition2=2' % (
 ', '.join(fields))

если ваш список условий будет расти, вы можете сделать то же самое, используя 'и' .join (conditions)
jcomeau_ictx

с вашим решением запрос будет еще труднее редактировать, чем с Option_4, и его также будет трудно читать.
ssoler

@ssoler, это зависит от того, как ты делаешь. Я объявляю несколько переменных в своих программах и вместо них использую массивы строк, что делает методы, подобные приведенным выше, очень полезными и удобными в обслуживании, по крайней мере, для меня.
jcomeau_ictx

-1

Я бы посоветовал придерживаться варианта 2 (я всегда использую его для более сложных запросов SELECT * FROM table), и если вы хотите распечатать его красиво, вы всегда можете использовать отдельный модуль .


-1

Для коротких запросов, которые могут уместиться в одну или две строки, я использую решение строкового литерала в решении, получившем наибольшее количество голосов выше. Для более длинных запросов я разбиваю их на .sqlфайлы. Затем я использую функцию-оболочку для загрузки файла и выполнения сценария, например:

script_cache = {}
def execute_script(cursor,script,*args,**kwargs):
    if not script in script_cache:
        with open(script,'r') as s:
            script_cache[script] = s
    return cursor.execute(script_cache[script],*args,**kwargs)

Конечно, это часто находится внутри класса, поэтому мне обычно не нужно передавать cursorявно. Я также обычно использую codecs.open(), но это дает общую идею. Тогда сценарии SQL полностью автономны в своих собственных файлах с собственной подсветкой синтаксиса.


-2
sql = """\
select field1, field2, field3, field4
from table
where condition1=1
and condition2=2
"""

[редактировать в ответ на комментарий]
Наличие строки SQL внутри метода НЕ означает, что вы должны "табулировать" ее:

>>> class Foo:
...     def fubar(self):
...         sql = """\
... select *
... from frobozz
... where zorkmids > 10
... ;"""
...         print sql
...
>>> Foo().fubar()
select *
from frobozz
where zorkmids > 10
;
>>>

ИМО, это то же самое, что и Option_2
ssoler

@ssoler: в вашем Option_2 во всех строках есть начальные пробелы ; обратите внимание, что в вашем примере перед этим не указаны начальные пробелы select. В моем ответе нет ведущих пробелов. Что привело вас к мнению, что они одинаковы?
John Machin

Если вы поместите свою строку sql в метод, вам придется табулировать все строки (Option_2). Одно из возможных решений - Option_3.
ssoler

@ssoler: Простите, я не понимаю этого замечания. Пожалуйста, посмотрите мой обновленный ответ.
Джон Мачин

Ваш обновленный ответ - мой вариант_3, не так ли? Мне не нравится этот вариант, потому что он нарушает ясность хорошо табулированного кода.
ssoler
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.