sqlite icon indicating copy to clipboard operation
sqlite copied to clipboard

Slow inserts

Open lemonrecognize opened this issue 3 years ago • 2 comments

Describe the bug Inserting 10k entries takes around 10 seconds

To Reproduce

db.execute(`
  CREATE TABLE IF NOT EXISTS someTable (
    fieldA TEXT,
    fieldB TEXT,
    fieldC TEXT
  );`
)

const statements = []

for (let i = 0; i < 10000; i++) {
  statements.push({
    statement: `INSERT INTO someTable VALUES (?,?,?);`,
    values: [
      'value A',
      'value B',
      'value C',
    ]
  })
}

db.executeSet(statements, true)

Expected behavior

Takes a lot less time to insert data

Smartphone (please complete the following information):

  • OS: Android

lemonrecognize avatar Oct 10 '22 09:10 lemonrecognize

@lemonrecognize sorry, nothing i can do about this. Have you try to compare the time with the time when you put a db.run inside the loop?

jepiqueau avatar Oct 11 '22 11:10 jepiqueau

@lemonrecognize i use androidx.sqlite.db

jepiqueau avatar Oct 11 '22 11:10 jepiqueau

@lemonrecognize nothing from your side so i close the issue

jepiqueau avatar Dec 08 '22 10:12 jepiqueau

I did notice a significantly slower experience on Android compared to iOS (40sec on Android vs 8sec on iOS) to insert about 10,000 records. I was using the following syntax with prepared statements.

INSERT INTO test_table (remote_id, message) VALUES (1,"msg"); INSERT INTO test_table (remote_id, message) VALUES (2,"msg"); INSERT INTO test_table (remote_id, message) VALUES (3,"msg");

I was able to speed it up to <2sec to insert on Android and iOS by using this syntax instead INSERT INTO test_table (remote_id, message) VALUES (1,"msg"),(2,"msg"),(3,"msg")...;

DGeoWils avatar Jun 22 '23 19:06 DGeoWils

@DGeoWils thanks for this i will have a look. May be you could make a PR which add a doc in the docs folder like PerformanceTricks which just descibed what you have done so it can benefit the developer's community

jepiqueau avatar Jun 23 '23 06:06 jepiqueau

@DGeoWils look at the executeSet command it has been done for this

jepiqueau avatar Jun 27 '23 09:06 jepiqueau

I had tried executeSet, and it was not faster than running the inserts the slow way I described above.

I was calling it in this manner:

executeSet([
  {statement: 'INSERT INTO test_table (remote_id, message) VALUES (?,?);', values: [1, "msg"]},
  {statement: 'INSERT INTO test_table (remote_id, message) VALUES (?,?);', values: [2, "msg"]},
  {statement: 'INSERT INTO test_table (remote_id, message) VALUES (?,?);', values: [3, "msg"]},
  ...
]);

However, I may be missing something in the documentation, or reading the code incorrectly. It seems as though I would need to pass a 2 dimensional array into the values parameter and call execute set like this to accomplish what I was able to do using my fast inserts above

executeSet([{
    statement: 'INSERT INTO test_table (remote_id, message) VALUES (?,?);', 
    values: [
        [1, "msg"],
        [2, "msg"],
        [3, "msg"],
        ...
     ]
}]);

Is this how executeSet should be called?

Edit:

After taking a look at the definition of execute set here (https://github.com/capacitor-community/sqlite/blob/57afbb64a1f50d4591c1f25134ac71d04f0f2bb7/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/Database.java#L353), it would appear that I could call executeSet in the second way, but this is not documented sufficiently for when I'm calling it with JS. I'd be happy to take a crack at a PR updating that documentation

DGeoWils avatar Jun 27 '23 19:06 DGeoWils

@DGeoWils yes it is the way you can call executeSet.in this way you open a transaction do all insert values and close the transaction which is exactelly the same that your proposed way. In the slowest way each time you do an insert for one value you open the transaction run the insert and commit the transaction.

jepiqueau avatar Jun 28 '23 04:06 jepiqueau

I looked through the java code for executeSet and it would definitely work. However in my use case I'm already looping over a blob of data to pull it into a few separate tables, so passing that into the executeSet function which loops over it a couple more times isn't what I want to do.

Good to have as a tool though

DGeoWils avatar Jul 06 '23 19:07 DGeoWils

In our case executeSet is about 30sec slower between version 5.0.5 and 4.8.0-1. I tested two iOS 16 simulator devices next to each other.

I am currently looking if its an issue with our queries or with capacitor 5 promise resolve...

muuvmuuv avatar Jul 19 '23 13:07 muuvmuuv

@muuvmuuv keep me a are. The executeSet command has not really change except by adding RETURNING support

jepiqueau avatar Jul 20 '23 06:07 jepiqueau

Seems to be #414 where I had set the transaction on executeSet to false… any idea why it is that slow in comparison?

As described in the issue, I run a lot of transactions in a loop. To be specific, sometimes it's 2000 transactions * 14 batches.

muuvmuuv avatar Jul 20 '23 08:07 muuvmuuv

I did notice a significantly slower experience on Android compared to iOS (40sec on Android vs 8sec on iOS) to insert about 10,000 records. I was using the following syntax with prepared statements.

INSERT INTO test_table (remote_id, message) VALUES (1,"msg"); INSERT INTO test_table (remote_id, message) VALUES (2,"msg"); INSERT INTO test_table (remote_id, message) VALUES (3,"msg");

I was able to speed it up to <2sec to insert on Android and iOS by using this syntax instead INSERT INTO test_table (remote_id, message) VALUES (1,"msg"),(2,"msg"),(3,"msg")...;

Thank you @DGeoWils! I was had write times taking anywhere from 60 - 100s on Android drop to 1s with this change Thanks for all your work on this plugin @jepiqueau!

davisCrabbBrandt avatar Sep 28 '23 17:09 davisCrabbBrandt