python 中 pymysql拼接執行入參踩坑(%跟%%的區別)總結

一、對於不同類型的入參,有不同的佔位符,但是用多了會發現,%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()。

 


    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章