使用 Django 将数千条记录插入 SQLite 表的有效方法是啥?
Posted
技术标签:
【中文标题】使用 Django 将数千条记录插入 SQLite 表的有效方法是啥?【英文标题】:What is an efficient way of inserting thousands of records into an SQLite table using Django?使用 Django 将数千条记录插入 SQLite 表的有效方法是什么? 【发布时间】:2010-11-11 06:55:48 【问题描述】:我必须使用 Django 的 ORM 将 8000 多条记录插入 SQLite 数据库。此操作需要作为 cronjob 大约每分钟运行一次。 目前我正在使用 for 循环遍历所有项目,然后将它们一一插入。 示例:
for item in items:
entry = Entry(a1=item.a1, a2=item.a2)
entry.save()
这样做的有效方法是什么?
编辑:两种插入方式的小对比。
没有 commit_manually 装饰器(11245 条记录):
nox@noxdevel marinetraffic]$ time python manage.py insrec
real 1m50.288s
user 0m6.710s
sys 0m23.445s
使用 commit_manually 装饰器(11245 条记录):
[nox@noxdevel marinetraffic]$ time python manage.py insrec
real 0m18.464s
user 0m5.433s
sys 0m10.163s
注意:除了插入数据库之外,test 脚本还执行一些其他操作(下载 ZIP 文件,从 ZIP 存档中提取 XML 文件,解析 XML文件),因此执行所需的时间并不一定代表插入记录所需的时间。
【问题讨论】:
【参考方案1】:你想签出django.db.transaction.commit_manually
。
http://docs.djangoproject.com/en/dev/topics/db/transactions/#django-db-transaction-commit-manually
所以应该是这样的:
from django.db import transaction
@transaction.commit_manually
def viewfunc(request):
...
for item in items:
entry = Entry(a1=item.a1, a2=item.a2)
entry.save()
transaction.commit()
这只会提交一次,而不是在每次 save() 时提交。
在 django 1.3 中引入了上下文管理器。 所以现在你可以以类似的方式使用 transaction.commit_on_success():
from django.db import transaction
def viewfunc(request):
...
with transaction.commit_on_success():
for item in items:
entry = Entry(a1=item.a1, a2=item.a2)
entry.save()
在 django 1.4 中,添加了bulk_create
,允许您创建模型对象的列表,然后一次性提交它们。
注意使用批量创建时不会调用保存方法。
>>> Entry.objects.bulk_create([
... Entry(headline="Django 1.0 Released"),
... Entry(headline="Django 1.1 Announced"),
... Entry(headline="Breaking: Django is awesome")
... ])
在 django 1.6 中,引入了 transaction.atomic,旨在替换现在的旧功能 commit_on_success
和 commit_manually
。
来自 django documentation on atomic:
atomic 既可用作装饰器:
from django.db import transaction
@transaction.atomic
def viewfunc(request):
# This code executes inside a transaction.
do_stuff()
作为上下文管理器:
from django.db import transaction
def viewfunc(request):
# This code executes in autocommit mode (Django's default).
do_stuff()
with transaction.atomic():
# This code executes inside a transaction.
do_more_stuff()
【讨论】:
这会将它们全部实例化为模型,并运行数千个单独的插入。我总是不得不使用 SQL 并为这种类型的卷进行手动批量插入; Django 不是为它而构建的。但是,是的,如果您这样做,您肯定想要单笔交易。 我没有 .net 经验,但从一般数据库的角度来看,关闭 AUTOCOMMIT 并在 BEGIN/END TRANSACTION 语句之间封装 INSERT 语句将比使用 AUTOCOMMIT 和单独运行 INSERTS 更快。请注意,这些命令及其使用方式可能会根据您使用的数据库而改变。如果您想要 .net 或 .net 框架特定的答案,请继续并开始一个新问题。 既然 Django 1.4 已经发布,使用docs.djangoproject.com/en/dev/ref/models/querysets/… 更有意义。另一种快速的替代方法是手动创建批处理 SQL 插入。这里的提示(在一个事务中提交)不会像在一个插入中发送一样快。 从 1.9 开始,bulk_create 运行良好。请注意,您需要将创建分解成批次,为 SQLite 添加的属性总数不超过 999 个。 值得注意的是transaction.atomic
不会使代码运行得更快。否则,很好的总结,谢谢。【参考方案2】:
在 Django 1.4 中可以进行批量创建:
https://django.readthedocs.io/en/1.4/ref/models/querysets.html#bulk-create
【讨论】:
它的工作方式与保存的工作方式相同吗?例如保存或更新每个对象?【参考方案3】:看看this。它仅适用于开箱即用的 mysql,但有一些关于如何处理其他数据库的指针。
【讨论】:
【参考方案4】:批量加载项目可能会更好 - 准备一个文件并使用批量加载工具。这将比 8000 个单独的插入更有效。
【讨论】:
【参考方案5】:要回答特别是关于 SQLite 的问题,如所问,虽然我刚刚确认 bulk_create 确实提供了巨大的加速,但 SQLite 有一个限制:“默认是在一批中创建所有对象,除了SQLite 默认情况下,每个查询最多使用 999 个变量。"
引用的内容来自文档 --- A-IV 提供了一个链接。
我要补充的是,alpar 的this djangosnippets 条目似乎也对我有用。它是一个小包装器,可将您要处理的大批量拆分为小批量,管理 999 个变量的限制。
【讨论】:
对于 Django ≥1.5,Django sn-p 应该是不必要的,对吧?从 Django 1.5 开始,您可以使用一个batch_size
参数:“batch_size 参数控制在单个查询中创建多少个对象。默认是在一个批次中创建所有对象,但 SQLite 除外,其中默认值是这样的每个查询最多使用 999 个变量。batch_size 参数是在 1.5 版中添加的。"【参考方案6】:
您应该查看DSE。我编写 DSE 来解决这类问题(大量插入或更新)。使用 django orm 是一条死路,您必须使用纯 SQL 来完成,而 DSE 会为您处理大部分工作。
托马斯
【讨论】:
另一件事;如果您决定使用普通 SQL,并且您插入的 SQL 每次都具有相同的字段,请尝试使用 cursor.executemany(SQL, [list of entries to insert])。比为每个条目运行插入要快得多。【参考方案7】:def order(request):
if request.method=="GET":
# get the value from html page
cust_name = request.GET.get('cust_name', '')
cust_cont = request.GET.get('cust_cont', '')
pincode = request.GET.get('pincode', '')
city_name = request.GET.get('city_name', '')
state = request.GET.get('state', '')
contry = request.GET.get('contry', '')
gender = request.GET.get('gender', '')
paid_amt = request.GET.get('paid_amt', '')
due_amt = request.GET.get('due_amt', '')
order_date = request.GET.get('order_date', '')
prod_name = request.GET.getlist('prod_name[]', '')
prod_qty = request.GET.getlist('prod_qty[]', '')
prod_price = request.GET.getlist('prod_price[]', '')
# insert customer information into customer table
try:
# Insert Data into customer table
cust_tab = Customer(customer_name=cust_name, customer_contact=cust_cont, gender=gender, city_name=city_name, pincode=pincode, state_name=state, contry_name=contry)
cust_tab.save()
# Retrive Id from customer table
custo_id = Customer.objects.values_list('customer_id').last() #It is return Tuple as result from Queryset
custo_id = int(custo_id[0]) #It is convert the Tuple in INT
# Insert Data into Order table
order_tab = Orders(order_date=order_date, paid_amt=paid_amt, due_amt=due_amt, customer_id=custo_id)
order_tab.save()
# Insert Data into Products table
# insert multiple data at a one time from djanog using while loop
i=0
while(i<len(prod_name)):
p_n = prod_name[i]
p_q = prod_qty[i]
p_p = prod_price[i]
# this is checking the variable, if variable is null so fill the varable value in database
if p_n != "" and p_q != "" and p_p != "":
prod_tab = Products(product_name=p_n, product_qty=p_q, product_price=p_p, customer_id=custo_id)
prod_tab.save()
i=i+1
return HttpResponse('Your Record Has been Saved')
except Exception as e:
return HttpResponse(e)
return render(request, 'invoice_system/order.html')
【讨论】:
这段代码运行正常。我被检查了我的水平。如果您身边发生任何错误,请再次检查代码并理解此代码的含义,然后再试一次。并且任何人都知道在数据库中一次插入多个数据的更好,更简单的方法,请与我们分享。谢谢 请在您的答案中直接包含澄清、进一步解释等,而不是使用comments。评论应用于询问更多信息或提出改进建议。【参考方案8】:def order(request):
if request.method=="GET":
cust_name = request.GET.get('cust_name', '')
cust_cont = request.GET.get('cust_cont', '')
pincode = request.GET.get('pincode', '')
city_name = request.GET.get('city_name', '')
state = request.GET.get('state', '')
contry = request.GET.get('contry', '')
gender = request.GET.get('gender', '')
paid_amt = request.GET.get('paid_amt', '')
due_amt = request.GET.get('due_amt', '')
order_date = request.GET.get('order_date', '')
print(order_date)
prod_name = request.GET.getlist('prod_name[]', '')
prod_qty = request.GET.getlist('prod_qty[]', '')
prod_price = request.GET.getlist('prod_price[]', '')
print(prod_name)
print(prod_qty)
print(prod_price)
# insert customer information into customer table
try:
# Insert Data into customer table
cust_tab = Customer(customer_name=cust_name, customer_contact=cust_cont, gender=gender, city_name=city_name, pincode=pincode, state_name=state, contry_name=contry)
cust_tab.save()
# Retrive Id from customer table
custo_id = Customer.objects.values_list('customer_id').last() #It is return
Tuple as result from Queryset
custo_id = int(custo_id[0]) #It is convert the Tuple in INT
# Insert Data into Order table
order_tab = Orders(order_date=order_date, paid_amt=paid_amt, due_amt=due_amt, customer_id=custo_id)
order_tab.save()
# Insert Data into Products table
# insert multiple data at a one time from djanog using while loop
i=0
while(i<len(prod_name)):
p_n = prod_name[i]
p_q = prod_qty[i]
p_p = prod_price[i]
# this is checking the variable, if variable is null so fill the varable value in database
if p_n != "" and p_q != "" and p_p != "":
prod_tab = Products(product_name=p_n, product_qty=p_q, product_price=p_p, customer_id=custo_id)
prod_tab.save()
i=i+1
【讨论】:
不鼓励像您这样的纯代码答案。【参考方案9】:我建议使用纯 SQL(不是 ORM),您可以通过一次插入来插入多行:
insert into A select from B;
只要结果与表 A 中的列匹配并且没有约束冲突,您的 sql 的 select from B 部分就可以像您希望的那样复杂。
【讨论】:
以上是关于使用 Django 将数千条记录插入 SQLite 表的有效方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章