插入 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)
我有 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)