Python SQL查询字符串格式


93

我正在尝试找到格式化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,字段3,字段4 \ N_ _ ___ 从表\ n _ ___ 其中条件1 = 1 \ n _ ___ _and条件2 = 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 2014年

Answers:


130

很抱歉张贴到这样的旧主题-但作为同时也对pythonic“ best”具有热情的人,我想我会分享我们的解决方案。

解决方案是使用python的String Literal Concatenation(http://docs.python.org/)构建SQL语句,该字符串可以限定为Option 2和Option 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. 它保留pythonic的“制表格式”格式,但不添加多余的空格字符(这会污染日志记录)。
  2. 它避免了选项4的反斜杠连续丑陋,这使添加语句变得困难(更不用说空格盲目了)。
  3. 而且,在VIM中扩展语句非常简单(只需将光标定位到插入点,然后按SHIFT-O即可打开新行)。

1
如果这是用于打印,我认为更好的选择是在输出之前将其写为"""textwrap.dedent()
mutiline

我使用了该选项,但它也使日志输出变为多行。跟踪数据库聊天应用程序时,这会导致大量输出。
user590028 2014年

1
这是一个旧线程,但是我一直将此格式用作最佳实践,但是对于较长的查询它会变得乏味
Jabda

7
我们是否不应该总是使用双引号"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'

如果condition的值应该是字符串,则可以这样:

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(),将有一种方法可以将a弹出';DROP TABLE Users到SQL中。看看PEP-249如何正确使用参数。python.org/dev/peps/pep-0249/#paramstyle
mattmc3

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;

蟒蛇

sql =('call example;')

-1

您可以将字段名称放入数组“ fields”,然后:


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

如果您的条件列表增加,则可以使用'和'.join(conditions)
jcomeau_ictx 2011年

使用您的解决方案,查询将比使用Option_4更加难以编辑,并且也难以阅读。
ssoler 2011年

@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
;
>>>

IMO这与Option_2相同
ssoler 2011年

@ssoler:您的Option_2在所有行上都有前导空格;请注意,您的示例省略了之前的前导空格select。我的答案没有前导空格。是什么导致您形成他们相同的观点?
John Machin

如果将sql字符串放入方法中,则必须将所有行制成表格(Option_2)。一种可能的解决方案是Option_3。
ssoler 2011年

@ssoler:对不起,我不明白这句话。请查看我更新的答案。
约翰·马钦

您更新的答案是我的Option_3,不是吗?我不喜欢此选项,因为它破坏了列表格式良好的代码的清晰度。
ssoler 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.