Django:批量运营

时间:2012-12-26 01:31:11

标签: python sql django orm bulk

商家:
我遇到了一个问题 - 当使用Django ORM操作大型数据集时,规范的方法是使用每个元素进行操作。但当然这种方式效率很低。所以我决定使用原始SQL。

药物:
我有一个基本代码,它形成SQL查询,更新表的行,并提交它:

from myapp import Model
from django.db import connection, transaction
COUNT = Model.objects.count()
MYDATA = produce_some_differentiated_data() #Creating individual value for each row
cursor = connection.cursor()
str = []
for i in xrange(1, COUNT):
    str.append("UPDATE database.table\n"
               "SET field_to_modify={}\n"
               "WHERE primary_key_field={};\n".format(MYDATA, i))


str = ''.join(str)
cursor.execute(str)
transaction.commit_unless_managed() #This cause exception

在最后一个声明中,即使SIZE很小,我也明白这一点:

_mysql_exceptions.ProgrammingError: (2014, "Commands out of sync; you can't run this command now")

也许Django不允许一次执行多个SQL查询?

PS 在提交之前关闭光标有助于避免异常,但这是正确的吗?

我的期望:
我正在寻找批量操作的每个可能的固体解决方案(最好是在Django内部)。 我不关心它是ORM还是原始SQL,我会支持上面粘贴的代码,如果我能避免错误的话。如果没有解决方案,至少,只是为了好奇,才能知道这个例外的原因。

除了答案我还学到了什么:
在Django 1.4中引入了bulk_create,用于有效的多个INSERT操作

3 个答案:

答案 0 :(得分:6)

Django 1.4+对其ORM中的批量操作提供了相当不错的支持,您应该看看是否可以使用它 - 它是最便携的方式,并且非常适合使用它。

它不仅允许更新所有对象中字段的相同值(这是微不足道的),而且还允许基于其他字段更新字段值以及执行一些有限的计算。我不确定它是否符合您的需求(取决于" produce_some_differentiated_data"有效) - 您可以做的一些计算,其中一些可能不行。一些例子:

image_id_list = [1,5,6]
Image.objects.filter(image_id__in=image_id_list).
     update(views_number=F('views_number') + 1)

上面的示例将转换为类似于SQL的SQL:

UPDATE image SET views_number = views_number + 1 WHERE image_id IN (1,5,6);

这是进行批量更新的最快方式 - 比运行多个查询更快。在单个SQL语句中运行多个查询并不能真正提高操作速度。改进它的方法是使像上面这样的单个查询同时在许多行上运行。您可以在更新语句中构建相当复杂的公式,以便在您的" produce_some_differentiated_data"方法可以用这种方式表达。即使不能直接完成,您也可以对模型进行一些修改并添加一些额外的字段来实现。如果经常执行这种批量操作,这可能会有所回报。

来自Django的文档:

  

Django支持使用加法,减法,乘法,   使用F()对象进行除法和模运算,两者都有常量   和其他F()对象。

更多相关信息: https://docs.djangoproject.com/en/dev/topics/db/queries/#updating-multiple-objects-at-once

答案 1 :(得分:5)

如果您需要原始SQL,请使用 cursor.executemany(query, param_list)

param_list = [("something_1", 1), ("something_2", 2),...]
# or everything like [(some_number_1, 1)...]. Apostrophes around the substituted
# "%s" and the complete necessary escaping is added automatically for string
# parameters.

cursor.executemany("""UPDATE database.table
        SET field_to_modify=%s
        WHERE primary_key_field=%s""",
        param_list)

它有很多优点:

  • 查询字符串比大查询短得多,可以快速分析/优化,而不会消耗数据库规划器不必要的资源。 (如果手动将参数解析为SQL,则会得到许多必须单独分析的不同SQL命令。)
  • 手动将字符串解析为SQL是一种不好的做法,因为如果您没有正确地从用户输入中转义意外的撇号和反斜杠,这可能是一个安全问题(SQL注入攻击)。

虽然Django-0.96到了目前1.5-beta。一个有用的类似答案是https://stackoverflow.com/a/6101536/448474

executemany方法在前几年有两个与异常处理相关的固定问题。因此,如果您故意导致数据库异常,execute(self, sql, params=())太多或太少等等,请验证您的Django版本是否会收到有用的错误消息。但是,几分钟的初始修补/测试比等待的许多小时更快对于慢速方法。

答案 2 :(得分:1)

您是否尝试过交易。

https://docs.djangoproject.com/en/dev/topics/db/transactions/

你需要这样的东西。

@transaction.commit_manually
def viewfunc(request):
    for row in rows:
        row.modify() # wherever you want to change
    transaction.commit()

问题不在于ORM开销,是数据库连接,避免这么多调用,并且每次执行几行提交。

在上面的示例中,您可以将行分成两半,或三分之一或十分之一等。