DELETE statement runs multiple times

Hi,

I execute the following code:

_=try Layer.deleteAll(db, keys: layers.map({$0.id!}))

I know that layers.count is only 1. Still the output in the debugger window shows that for a small database this query is still run multiple times. This seems to be caused by the code in Database+Statements.swift:250.
It seems it consumes rows from SQLite, but why would a simple DELETE statement return multiple rows (for a larger database more rows are returned). The SQLite DELETE documentation doesn't mention anything about returned rows.

Thanks in advance.

Kind regards,

Remco Poelstra

Hello @remcopoelstra. This question has already been asked in the Github issues: https://github.com/groue/GRDB.swift/issues/624

Github issues are a wonderful source of experience for GRDB users: it's often worth searching through them before asking a question.

You're absolutely right. I searched the forum, but will also check the GitHub issues next time.
It seems that the trace is output for every foreign key constraint that's cascaded.
Leaves me wondering why I can insert 45000 rows in a matter of seconds, but deleting these rows takes about 2 minutes. This is a pure SQLite problem though, as the command line shows the same behaviour.

2 minutes is alarmingly slow.

If you do not wrap all those deletions in a single database transaction, consider doing it. It should considerably improve the performance.

And if you still experience odd performances, use Instruments and profile your app!

I either delete (in my app) a single row from the parent table (where the foreign key constraint cascades the delete) or perform the equivalent on the command line: DELETE FROM elements WHERE layerId = 1. Both take about the same time.

I added an index on layerId but that didn't help at all.

I did a profile in instruments and it says that all this time is spend in sqlite3_step(). The .timer on feature of the command line confirms this (shows about the same time spend).

OK. And the index should be a good idea. But do you make sure those deletions are wrapped in a single transaction, as suggested?

I believe it is, as the 'full' code is:

try database.write { db in
    _=try Layer.deleteAll(db, keys: layers.map({$0.id!}))
}

In my test case, the layers.count is 1.

Maybe you're not familiar with GRBD transaction handling. Compare:

// N transactions
try dbQueue.write { db in
    try Layer.delete...
}
try dbQueue.write { db in
    try Layer.delete...
}
...
try dbQueue.write { db in
    try Layer.delete...
}

// One transaction (faster)
try dbQueue.write { db in
    try Layer.delete...
    try Layer.delete...
    ...
    try Layer.delete...
}