插入 sqlite 数据库花费太多时间 - iOS

inserting into sqlite database taking too much time - iOS

我有 6000 条电话簿记录要插入到 sqlite 中 它需要 45 秒,那是很长的时间。

对于每条记录,我只需要几个属性,如名称、电子邮件、ID、修改日期。所以我至少需要一个 for 循环,因为它需要 45 秒。 我怎样才能减少?

这是更新后的代码(此代码是 dispatch_async 中的 运行)

我也检查了类似的问题How to insert 40000 records fast into an sqlite database in an iPad解决方案说我必须使用我已经使用过但仍然面临相同问题的 BEGIN & END 事务。

UPDATE - 根据建议的解决方案,我已经更新了我的代码,但仍然需要 45 秒。请帮助我。

    sqlite3_exec(db.insertPerson, "BEGIN TRANSACTION", nil, nil, nil)

    for record:ABRecordRef in contactList
    {


        contactNumber = ""
        email = ""
        fullName = ""


        if (ABRecordCopyValue(record,kABPersonPhoneProperty) != nil) && (ABRecordCopyValue(record,kABPersonFirstNameProperty) != nil)

        {

                firstName = (ABRecordCopyValue(record, kABPersonFirstNameProperty)?.takeRetainedValue() as? String)!

                let numbers:ABMultiValue = ABRecordCopyValue(record, kABPersonPhoneProperty).takeRetainedValue()

               if (ABMultiValueGetCount(numbers) > 0)
               {
                    contactNumber = (ABMultiValueCopyValueAtIndex(numbers,0)?.takeRetainedValue() as? String)!

               }


                let modificationNSDate = (ABRecordCopyValue(record, kABPersonModificationDateProperty)?.takeRetainedValue())! as! NSDate


                modificationDate = dateFormatter.stringFromDate(modificationNSDate)

                recordId = ABRecordGetRecordID(record)

               if (ABRecordCopyValue(record,
                   kABPersonLastNameProperty) != nil)
               {

                   lastName = (ABRecordCopyValue(record,
                        kABPersonLastNameProperty).takeRetainedValue()as? String)!

                }


                let emails: ABMultiValueRef = ABRecordCopyValue(record, kABPersonEmailProperty).takeRetainedValue()

                for (var i = 0; i < ABMultiValueGetCount(emails); i++)
                {
                    email = ABMultiValueCopyValueAtIndex(emails, i).takeRetainedValue() as! String

                }


        }


        fullName = "\(firstName) \(lastName)";
        lastName = "";


   db.insertIntoContact(contactName: fullName, contactNumber: contactNumber, contactEmail: email, recordid : recordId, modifieddate: modificationDate)

    }

    sqlite3_exec(db.insertPerson, "END TRANSACTION", nil, nil, nil)

这里是 insertIntoContact 函数.

  func insertIntoContact(contactName contactName : String!, contactNumber : String!, contactEmail : String!, recordid:Int32!, modifieddate:String! ) -> Bool
   {
    sqlite3_bind_text(insertPerson, 1, (contactName as NSString).UTF8String, -1, nil)
    sqlite3_bind_text(insertPerson, 2, (contactNumber as NSString).UTF8String, -1, nil)
    sqlite3_bind_text(insertPerson, 3, (contactEmail as NSString).UTF8String, -1, nil)
    sqlite3_bind_int(insertPerson, 4, Int32(recordid))
    sqlite3_bind_text(insertPerson, 5, (modifieddate as NSString).UTF8String, -1, nil)
    return executeUpdate(sqlStatement: insertPerson)
}

更多详情

func executeUpdate(sqlStatement statement:COpaquePointer) -> Bool
    {
        let resultCode = executeStatement(sqlStatement: statement, success:Int(SQLITE_DONE))
        sqlite3_reset(statement)
        return resultCode
    }



func executeStatement(sqlStatement statement:COpaquePointer,success successConstant:Int) -> Bool
{
    let success = Int(sqlite3_step(statement))

    if success != successConstant
    {
        print("Statement \(successConstant) failed with error \(success)")
        return false
    }

    return true
}

您需要在开始遍历 6000 条记录之前使用 BEGIN TRANSACTION 并在发出所有条目添加之后使用 END TRANSACTION - 这样您将降低 I/O 负载并使事情发生更快。

第一个问题(NSDateFormatter 分配):

您正在为每个循环创建 NSDateFormatter 的新实例。这意味着你创建了 6000 次.....创建一个实例真的很昂贵。所以把它移出循环。 (参见下面的代码示例)

第二个问题(使用交易):

那么您需要按照之前的回答建议的在此时开始交易。循环浏览联系人后,您可以按照上一个答案的建议结束交易。

一些需要更好错误检查的伪代码:

我已将所有与 sqlite 相关的函数放入循环中,以便更容易查看到底发生了什么。但是您确实需要找出什么需要时间,因为您应该已经看到使用事务的性能提高。

struct Contact
{
    let name: String
    let number: String
    let email: String
    let modificationDate: String
    let id: Int32
}

从 ABRecordRef 获取联系人的方法

func contactFromABRecordRef(record: ABRecordRef, dateFormatter: NSDateFormatter) -> Contact?
{
    var email = ""
    var contactNumber = ""
    var firstName = ""
    var lastName = ""
    var modificationDate = ""
    var id: Int32 = -1

    if (ABRecordCopyValue(record, kABPersonPhoneProperty) != nil)
    {
        let modificationNSDate = (ABRecordCopyValue(record, kABPersonModificationDateProperty)?.takeRetainedValue())! as! NSDate
        modificationDate = dateFormatter.stringFromDate(modificationNSDate)
        id = ABRecordGetRecordID(record)

        let numbers: ABMultiValue = ABRecordCopyValue(record, kABPersonPhoneProperty).takeRetainedValue()
        if (ABMultiValueGetCount(numbers) > 0)
        {
            contactNumber = (ABMultiValueCopyValueAtIndex(numbers,0)?.takeRetainedValue() as? String)!
        }

        if (ABRecordCopyValue(record, kABPersonFirstNameProperty) != nil)
        {
            firstName = (ABRecordCopyValue(record, kABPersonFirstNameProperty)?.takeRetainedValue() as? String)!
        }

        if (ABRecordCopyValue(record, kABPersonLastNameProperty) != nil)
        {
            lastName = (ABRecordCopyValue(record, kABPersonLastNameProperty).takeRetainedValue()as? String)!
        }

        let emails: ABMultiValueRef = ABRecordCopyValue(record, kABPersonEmailProperty).takeRetainedValue()
        for (var i = 0; i < ABMultiValueGetCount(emails); i++)
        {
            email = ABMultiValueCopyValueAtIndex(emails, i).takeRetainedValue() as! String
        }

        return Contact(name: "\(firstName) \(lastName)", number: contactNumber, email: email, modificationDate: modificationDate, id: id)
    }

    return nil
}

将循环更新为类似于您的循环

// Load your contact list from here
let contactList: [ABRecordRef] = []
let dateFormatter: NSDateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

sqlite3_exec(db, "BEGIN TRANSACTION", nil, nil, nil)
var statement: COpaquePointer = nil
if sqlite3_prepare_v2(db, "insert into contacts values (?1, ?2, ?3, ?4, ?5)", -1, &statement, nil) != SQLITE_OK
{
    let errmsg = String.fromCString(sqlite3_errmsg(db))
    // Handle the error message here!!!!
    print("Error when preparing statement: ", errmsg)
}

for record: ABRecordRef in contactList
{
    if let contact = contactFromABRecordRef(record, dateFormatter: dateFormatter)
    {
        sqlite3_bind_text(statement, 1, (contact.name as NSString).UTF8String, -1, nil)
        sqlite3_bind_text(statement, 2, (contact.number as NSString).UTF8String, -1, nil)
        sqlite3_bind_text(statement, 3, (contact.email as NSString).UTF8String, -1, nil)
        sqlite3_bind_int(statement, 4, Int32(contact.id))
        sqlite3_bind_text(statement, 5, (contact.modificationDate as NSString).UTF8String, -1, nil)

        if sqlite3_step(statement) != SQLITE_DONE
        {
            let errmsg = String.fromCString(sqlite3_errmsg(db))
            // Handle the error message here!!!!
            print("Error when stepping through statement: ", errmsg)
        }

        sqlite3_reset(statement)
    }
}

if sqlite3_exec(db, "COMMIT TRANSACTION", nil, nil, nil) != SQLITE_OK
{
    let errmsg = String.fromCString(sqlite3_errmsg(db))

    print("Error when commiting transaction: ", errmsg)
}

sqlite3_finalize(statement)