Here, the query and transaction are created locally. Is this customary?
I used to use TSQLQuery component dropped on the DataModule (let's say its name is SQLQuery1). But this method had risk of "nesting the same TSQLQuery and TSQLtransaction, when the function is called within another function that uses the same transaction (if not the same TSQLQuery). So I'm moving to locally creating query and transaction components.
Yes, this is customary. Precisely for the reason you mention.
Although I would make it a standalone function, passing the database, in which case it can even be used for different database connections.
But making it a function in the datamodule is also possible.
PS. I would build in some error checking. If your query doesn't return a result it will generate an exception at the moment (at the Fields[0] which doesn't exist when there is no resultset). That could be intentional but you need to handle the exception a step higher then.
PPS. I also would make such function a bit more generic. For example also passing which fieldname you want to return.
But I guess you get the point... standalone functions like this will greatly reduce the chances for errors in using the wrong TSQLQuery and messing things up.
PPPS. I also have some procedure which change the database with insert, update and delete. In that case you also need to pass a TSQLTransaction so the update is done in the correct transaction flow for the rest of your program.