在 django 原始 sql 中将列表或元组作为参数传递

问题描述 投票:0回答:5

我有一个列表,想通过 django 原始 sql 传递。

这是我的清单

region = ['US','CA','UK']

我在这里粘贴了一部分原始sql。

results = MMCode.objects.raw('select assigner, assignee from mm_code where date between %s and %s and country_code in %s',[fromdate,todate,region])

现在,当我在 django python shell 中执行它时,它给出了以下错误

Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/local/lib/python2.6/dist-packages/django/db/models/query.py", line 1412, in __iter__
query = iter(self.query)
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/query.py", line 73, in __iter__
self._execute_query()
File "/usr/local/lib/python2.6/dist-packages/django/db/models/sql/query.py", line 87, in _execute_query
self.cursor.execute(self.sql, self.params)
File "/usr/local/lib/python2.6/dist-packages/django/db/backends/util.py", line 15, in execute
return self.cursor.execute(sql, params)
File "/usr/local/lib/python2.6/dist-packages/django/db/backends/mysql/base.py", line 86, in execute
return self.cursor.execute(query, args)
File "/usr/lib/pymodules/python2.6/MySQLdb/cursors.py", line 166, in execute
self.errorhandler(self, exc, value)
File "/usr/lib/pymodules/python2.6/MySQLdb/connections.py", line 35, in defaulterrorhandler
raise errorclass, errorvalue
DatabaseError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ')' at line 1")

我也尝试过传递元组,但没有用。有人可以帮助我吗?

谢谢 维克拉姆

sql django list tuples
5个回答
39
投票

至少对于 PostgreSQL,列表/元组参数在 SQL 中被转换为数组,例如

ARRAY['US', 'CA', 'UK']

将其插入给定查询时,会导致无效的 SQL -

SELECT assigner, assignee FROM mm_code
WHERE date BETWEEN '2014-02-01' AND '2014-02-05'
AND country_code IN ARRAY['US', 'CA', 'UK']

但是,SQL 中的 'in' 子句在逻辑上等同于 -

SELECT assigner, assignee FROM mm_code
WHERE date BETWEEN %s AND %s
AND country_code = ANY(%s)

...当这个查询填入参数时,生成的 SQL 是有效的并且可以工作 -

SELECT assigner, assignee FROM mm_code
WHERE date BETWEEN '2014-02-01' AND '2014-02-05'
AND country_code = ANY(ARRAY['US', 'CA', 'UK'])

我不确定这是否适用于其他数据库,以及这是否会改变查询的计划方式。


19
投票

将列表转换为元组在 Postgres 中确实有效,尽管相同的代码在 sqlite3 下失败并带有

DatabaseError: near "?": syntax error
,所以看起来这是特定于后端的。你的代码行将变成:

results = MMCode.objects.raw('select assigner, assignee from mm_code where date between %s and %s and country_code in %s',[fromdate,todate,tuple(region)])

我在一个干净的 Django 1.5.1 项目上进行了测试,在 bar/models.py 中包含以下内容:

from django.db import models

class MMCode(models.Model):
    assigner = models.CharField(max_length=100)
    assignee = models.CharField(max_length=100)
    date = models.DateField()
    country_code = models.CharField(max_length=2)

然后在外壳处:

>>> from datetime import date
>>> from bar.models import MMCode
>>> 
>>> regions = ['US', 'CA', 'UK']
>>> fromdate = date.today()
>>> todate = date.today()
>>> 
>>> results = MMCode.objects.raw('select id, assigner, assignee from bar_mmcode where date between %s and %s and country_code in %s',[fromdate,todate,tuple(regions)])
>>> list(results)
[]

(请注意,此处的查询行略有更改,以使用 Django 创建的默认表名称,并在输出中包含

id
列,以便 ORM 不会抱怨)


4
投票

这不是一个很好的解决方案,因为您必须确保您的“区域”值正确转义为 SQL。然而,这是我可以使用 Sqlite 的唯一方法:

sql = ('select assigner, assignee from mm_code '
    'where date between %%s and %%s and country_code in %s' % (tuple(region),))
results = MMCode.objects.raw(sql, [fromdate,todate])

4
投票

我今天就遇到了这个问题。 Django 已经改变(我们现在有

RawSQL()
和朋友!),但总体解决方案仍然是相同的。

根据 https://stackoverflow.com/a/283801/532513,总体思路是显式向 SQL 字符串添加与

region
数组中元素相同数量的占位符。

您的代码将如下所示:

sql = 'select assigner, assignee from mm_code where date between %s and %s and country_code in ({0})'\
      .format(','.join(['%s'] * len(region)))
results = MMCode.objects.raw(sql, [fromdate,todate] + region)

您的 sql 字符串将首先变为

... between %s and %s and country_code in (%s, %s, %s) ...
,并且您的参数将有效地变为
[fromdate, todate, 'US', 'CA', 'UK']
。这样,您就可以让数据库后端正确转义并可能对每个国家/地区代码进行编码。


-2
投票

我不反对原始sql,但你可以使用:

MMCode.objects.filter(country_code__in=region, date__range=[fromdate,todate])

希望这有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.