Note that the commitment happens in one unit, at the Save() invocation rather than within two insert statements. We get to assemble the form and all of its lines before storing anything to the database, so a mistake on our part isn’t going to result in a “headless” set of form lines or a form with no lines.
The assembly also happens in one neat, piece-by-piece, easy-to-comprehend sequence rather than two complex bundles. This is much easier to understand, and so to debug; and it’s easier to optimise, which makes it tend to run faster, overall.
The next developmental accelerator is that copying and changing the slab of code to face a new kind of form is relatively simple, possibly as simple a defining a new type of form object and changing the form names in the code. It’s an acknowledgement that the process is the same in both cases, only the details (and maybe not many of those) changed between versions.
For scores or dozens of users on the same server, Caché adds up to an immense performance difference.
Relating effectively to this
If you wanted this to run with a relational database, you’d simply code the relational parts into the Save() invocation rather than rewriting all of the instances of code which created the forms in the first place. You still do write the relational code, but it’s tucked away in a single “background” module rather than spread all across the application. This means that there’s only one carefully isolated set of changes to debug, rather than one set for every form-creation section in the program.
Another thing which Caché will have done is... well... cached the results. What this means is that doing something typical like print this out next will not result in another full database access, just an integrity check and an access from memory.
For one individual user, the cache impact won’t be huge, but for scores or dozens of users on the same server, it adds up to an immense performance difference. Even more performance advantages result from processing related objects. Caché can see “between” the records it’s managing, so a run of related objects is likely to have other records in the same chunk of data, resulting in fewer fetches to retrieve the same fields – or to put it another way, it goes faster.
Speed through structure
The print-run above simply recovered records that were recently stored and remained in memory (if not the workstation’s memory, then at least the server’s). This effect relates to records stored earlier: when Caché fetches a chunk of data, if will often include “adjacent” records (however “adjacent” works out in this particular database) within the same fetching action, which records can also be iterated through with simple integrity checks.
On top of this, because Caché knows a little more about the structure of the database than a “flat” set of tables would reveal, it can organise the records (and fields, indexes, etc) to result in them being included with related fetches more often. This is the kind of restructuring which the really large databases can get away with by taking low-level advantage of their “ownership” of the storage medium to use techniques like “striping” potentially-related data across adjacent disk tracks.
Caché does its own optimisation without having to be quite so aware of physical items like heads or tracks, which implies much less management effort as a whole, although I personally wouldn’t look forward to recovering a damaged drive from either system.