I would do all of this with a timer script that runs the repetitive SQL, fans out the raw data to the target UDTs (recipients would be memory tags, not expressions), and perform all of the "heavier lifting", also writing to target memory tags. Multiple driving SQL queries would each get their own timer event. This approach has minimum efficiency lost to multi-threading and minimum skew between data arrival and final results distribution. Timer events naturally prevent overrunning, so final target results are sure to be delivered before the next query is run (if anything bogs down the DB or GW). Be sure to optimize tag writes with lists of tags and values, and use .writeSynchronous/writeBlocking to keep each event deterministic.
With the above design, there wouldn't be a tag change script. (Which is good, since there's no way outside the designer to update the list of subscribed tags.) I'd simply cache a list of UDT instances in the script module and reference it in the query event. Let your UDT-searching script populate/update that list for the query event(s) to use.
In a pinch, if you really must, you can use the gateway's context to access the tag manager, and add and remove your own tag change listeners. There be dragons that way, though: