一、對於不同類型的入參,有不同的佔位符,但是用多了會發現,%s纔是真理
二、sql拼接可以用%s,也可以用format,如果不考慮sql注入風險問題,個人建議使用format,可以接受dict爲入參進行匹配。
三、在考慮sql注入風險的情況下,可以將拼接好的sql跟入參分開,調用cur.execute(sql, params)來規避sql注入風險。
需要注意的是,這裏有兩種使用方式,建議使用第二種:
1、採用sql拼接的方式,拼接時用 xx= %s的方式,入參格式爲元組或者list
如下舉例:
sql = "select count(*) as sys_num from test where true "
params = []
if param1:
sql += " and param1 = %s "
params.append(param1)
if param2:
sql += " and (param2=%s) "
params.append(param2)
cur.execute(sql, params)
2、採用sql拼接的方式,拼接時用 xx=%(xx)s的方式, 入參格式爲dict
如下舉例:
sql = "select count(*) as sys_num from test where true "
params = {"param1": "xx", "param2": "xxx"}
if param1:
sql += " and param1 = %(param1)s "
if param2:
sql += " and (param2=%(param2)s) "
cur.execute(sql, params)
四、需要特別注意的是,當你的sql中包含了date_format(date,'%Y-%m')等帶%號的時候,有以下幾種情況需要注意:
1、如果你將sql完全拼接好(包括入參)再通過只傳入sql調用cur.execute(sql,param)方法執行的時候(這裏的param默認爲None),這裏的'%Y-%m'不需要改成'%%Y-%%m'。
如:
sql = """
SELECT * FROM test
WHERE date_time IN (SELECT max(date_time) FROM test)
and date_format(submit_date,'%Y') = date_format('{}','%Y')
GROUP BY status;
""".format(date_time)
cur.execute(sql)
# cur.execute(sql, {})
result = cur.fetchall()
需要注意:上面的cur.execute(sql)如果改成cur.execute(sql,param)並且param不爲None, 這時候就需要改成%%。
2、如果你拼接的sql不包括入參(而是通過%s或者其他佔位符),再通過傳入sql和param來調用cur.execute(sql,param)執行的時候,這裏的'%Y-%m'需要改成'%%Y-%%m'。
sql = """
SELECT * FROM test
WHERE date_time IN (SELECT max(date_time) FROM test)
and date_format(submit_date,'%%Y') = date_format(%(date_time)s,'%%Y')
GROUP BY status;
"""
cur.execute(sql, {"date_time": '2019-08-09'})
result = cur.fetchall()
3、爲什麼呢?通過查看源碼發現,cur.execute(sql,param)方法param默認爲None,當param爲None時,默認你傳入的sql是完整的,不進行預編譯,所以不需要兩個%%來防止被編譯。
當param不爲None時,默認你的sql是不完整的,需要通過params對你的sql進行匹配獲得完整的sql,這時會對sql進行預編譯,預編譯會對%x等佔位符進行處理,這時你的sql中有%Y,%m等不合法的佔位符
就會直接報錯,所以這種情況下需要使用%%來防止被編譯。
五、需要注意的是,當你的sql想動態接收數據庫表名或者字段名時,不能使用sql+params調用cur.execute(sql, {"table_name": 'aaaaa'})這種方式,因爲pymysql會判斷入參類型,如果是字符串的會補上'',表名加''會導致sql執行報錯。
如果一定要動態接收表名或者字段名,需要在調用cur.execute()前提前拼接好,可以使用%(table_name)s或者format()。
六、總結:
1、佔位符儘量都使用%s
2、建議使用防止sql注入的方式,通過將分開的sql,param作爲入參調用cur.execute(sql,param),這裏的param可以爲元組、列表和字典。
3、使用%%是爲了防止被mysql預編譯導致報錯,cur.execute(sql,param)方法中params默認爲None,不進行編譯,當params不爲None時,就會對你的sql進行預編譯(無論你的sql是否已經拼接完整)
這時就需要使用%%來防止被編譯。
4、動態接收表名或者字段名不能sql+params調用cur.execute(sql, params)這種方式,需要在調用cur.execute()前提前拼接好,可以使用%(table_name)s或者format()。