使用 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_successcommit_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 表的有效方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Wordpress 插入数千条记录

如何快速将40000条记录插入iPad中的sqlite数据库

如何使用 sqlite 批量插入超过 1000 条记录?

使用Hibernate进行数千次插入时的CPU利用率很高

将第二条记录添加到数据库后,代号为一个 Sqlite 问题

将数千条记录从单个 txt 文件导入数据库 MySQL 的更快方法?