使用Django将数千条记录插入SQLite表的有效方法是什么?


72

我必须使用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

注意:除了将脚本插入数据库(下载ZIP文件,从ZIP存档中提取XML文件,解析XML文件)之外,测试脚本还执行其他一些操作,因此执行所需的时间不一定代表插入所需的时间。记录。

Answers:


126

您想退房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添加了它,使您可以创建模型对象的列表,然后一次提交所有对象。

注意使用批量创建时,不会调用save方法。

>>> 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_successcommit_manually

atom的django文档中

原子既可以用作装饰器:

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()

8
这会将它们全部实例化为模型,并运行数千个单独的插入。我一直不得不使用SQL并为这种类型的卷进行手动批量插入。Django不是为此而构建的。但是,是的,如果您这样做的话,您肯定希望进行一次交易。
格伦·梅纳德

2
我没有.net经验,但是从一般的数据库角度来讲,关闭AUTOCOMMIT并在BEGIN / END TRANSACTION语句之间封装INSERT语句将比使用AUTOCOMMIT和单独运行INSERTS更快。请注意,这些命令及其用法可能会根据您使用的数据库而有所不同。如果要特定于.net或.net框架的答案,请继续并提出一个新问题。
monkut

3
现在Django 1.4已经发布,使用docs.djangoproject.com/en/dev/ref/models/querysets/…更加有意义。另一种快速的替代方法是手动创建批处理SQL插入。这里的提示(一次交易)将不如一次插入快。
Ben Regenspan 2012年

2
从1.9开始,bulk_create运行良好。请注意,您需要将创建过程分成多个批次,而SQLite的添加属性总数不超过999个。
Ezekiel Kruglick '16

1
值得注意的是,transaction.atomic这不会使代码运行得更快。否则,非常好的总结,谢谢。
Owen



3

您最好不批量加载项目-准备文件并使用批量加载工具。这将比8000个单独的刀片效率更高。


3

要回答这个问题,特别是关于SQLite的问题,正如我刚才所确认的那样,尽管我刚刚确认bulk_create确实提供了极大的提速,但是SQLite有一个局限性:“默认情况下,是成批创建所有对象,但SQLite除外。默认值是每个查询最多使用999个变量。”

引用的内容来自文档-A-IV提供了链接。

我要补充的是,alpar的djangosnippets条目似乎也对我有用。这是一个小包装,它将您要处理的大批次分成较小的批次,管理999个变量限制。


如果Django≥1.5,则不需要Django代码段,对吗?从Django 1.5开始,batch_size您可以使用一个参数:“ batch_size参数控制在单个查询中创建多少个对象。默认值是在一个批处理中创建所有对象,但SQLite除外,默认值是最大999。使用每个查询的变量。在1.5版中添加了batch_size参数。”
麦克风

2

您应该签出DSE。我写了DSE来解决这类问题(大量插入或更新)。使用django orm是死胡同,您必须使用简单的SQL来完成,而DSE会为您处理很多事情。

汤玛士


1
另一件事; 如果决定使用普通SQL,并且每次插入的SQL都具有相同的字段,请尝试使用cursor.executemany(SQL,[要插入的条目列表])。比每个条目运行一个插入要快得多。
Weholt 2011年

-1
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')

此代码运行正常。我已经检查过了。如果您身边发生任何错误,请再次检查代码并了解该代码的含义,然后重试。而且任何人都知道一次将多个数据插入数据库的更好,更简便的方法,请与我们分享。谢谢
Mohit Mishra

1
请在您的答案中直接添加说明,进一步的解释等,而不要使用注释。评论应用于寻求更多信息或提出改进建议。
乔伊

-1
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

不鼓励像您这样的仅代码答案。
加百利

-2

我建议使用普通SQL(不是ORM),您可以一次插入多个行:

insert into A select from B;

从B选择,你想让它变得只要结果符合表A中的列,并且没有约束冲突,可以为复杂的SQL的一部分。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.