2つのパラメーターを持つSQLの文字列を使用して、Djangoモデルで生のクエリを実行しています。ヘッドスクラッチャーは、文字列置換を使用してパラメーターを入力すると、パラメーター置換を使用して入力した場合よりも多くの結果が得られることです。後者の場合、結果は正しく、不完全です。コードは次のようになりますが、唯一の変更点は、正確なSQLを省略したことです。
# I have a long and ornate SQL statement that looks basically like this.
sql = "SELECT blah blah WHERE something = %s AND something_else in (%s)"
# If I do a raw query with string substitution I get more results (22) ...
sqlInsecureFilled = sql % (divID, storeRestrictStr)
promos_insecured = Promotions.objects.raw(sqlInsecureFilled)
# ... than if I use a parameterized raw query, which produces (10)
promos_secure = Promotions.objects.raw(sql, [divID, storeRestrictStr])
しかし、それはさらに奇妙になります。コマンドラインからこれを行う場合、raw_queryオブジェクト(つまり、promos_secure.query)からSQLを取得し、それをSequel Proターミナルにコピーすると、両方のクエリで同じ数の結果が生成されます-22!そしてまだ:
In [35]: [len(list(promos_insecured)), len(list(promos_secure))]
Out[35]: [22, 10]
要約すると、クエリは目で見て同じように見え(かなり長いため、正確に判断するのは困難です)、promos_xx.query文字列がMySQLターミナルにコピーされると、完全な結果セットが生成されます。それでも、上記のように実行すると、パラメーター化されたバージョンは10の結果を返しますが、他のバージョンは完全な22の結果を返します。
完全を期すために、ここにpromos_secure.queryがあります(promos_insecuredは同じです):
SELECT DISTINCT promotion_id, promotion_name, promotion_up_date, promotion_down_date, promotion_asset_id, promotion_notes, promotion_promo_id FROM promotions, promo_detail WHERE promotion_id = promo_detail_promotion_id AND promotion_start_date < now() AND promotion_end_date > now() AND promo_detail_cust_division_id = 1 AND promo_detail_not_expired = 1 AND promo_detail_store_id in (8214, 8217, 4952, 8194, 8198, 8162, 5010, 5011, 5012, 8219, 8182, 5048, 5076, 5095, 5096, 5102, 5109, 5131, 5156, 5160, 5161, 5165, 5166, 5173, 5182, 5198, 5200, 5201, 5202, 5203, 5227, 5228, 5229, 5230, 5232, 5233, 5234, <bunch of other comma-separated numbers omitted>, 9281) ORDER BY promotion_end_date ASC
編集:多分これは何が起こっているのか、そしてなぜそれが奇妙なのかを示すための最も簡潔な方法です:
promo_u = promotions.models.Promotions.objects.raw(sql % (1, storeRestrictStr))
promo_s = promotions.models.Promotions.objects.raw(sql, (1, storeRestrictStr))
pid_u = [s.promotion_id for s in promo_u]
pid_s = [s.promotion_id for s in promo_s]
In [76]: [len(list(pid_u)), len(list(pid_s))]
Out[76]: [22, 10]
# You can see the smaller number of results is a subset of the larger.
In [77]: [pid in pid_u for pid in pid_s]
Out[77]: [True, True, True, True, True, True, True, True, True, True]
# The larger number results shows no obvious pattern as to why they're missing.
In [87]: [pid in pid_s for pid in pid_u]
Out[87]: [False, False, False, False, True, False, False, False, True, True, True, True,
True, False, True, False, False, True, False, True, False, True]