@MarkMLl: I agree, the best solution comes from the database itself. At least I assume that, they've put a lot more work and knowhow into the database-code, than I'll ever do with my client-code...
That poat was only a sidenote to TRon's post...
I did tack on a couple of additional points but was in a rush to get out and it didn't get posted properly.
The first is that while transaction isolation is the "correct" way to go, how to actually set it up is not very well standardised from the POV of a client API and one might find onesself having to use a text parameter:
SQLTransaction1.Params.Clear;
scratch := '';
if ListBoxIsolation.ItemIndex >= 0 then
scratch := '''' + Trim(ListBoxIsolation.Items[ListBoxIsolation.ItemIndex]) + '''';
if scratch <> '' then
SQLTransaction1.Params.Append('default_transaction_isolation=' + scratch);
SQLQuery1.Open; (* Remaining components auto-activated *)
The second is that graphical database controls are fundamentally incompatible with long-running transactions: if there's e.g. a network glitch during the lifetime of a transaction the application program has no way of recovering. I explored that in conjunction with PostgreSQL at
https://github.com/MarkMLl/testdb/tree/main, and since my findings were basically the same as those reported by somebody running a different backend I anticipate that they will be much the same for everything.
That was, TBH, something I'd anticipated for a long time but hadn't actually experimented with. The problem is that SQL was originally a "mainframe thing", and the designers anticipated that it would be used by "middleware" rather than directly by a remote client application.
MarkMLl