Notice how, in one of the examples above, a string is (easily ...) constructed that consists of one-or-more question marks separated by commas. This string is inserted dynamically (and safely) into the SQL string. A corresponding parameter is added to the list of parameters at the same time, and the two are provided together when the query is run. This technique is suitably dynamic, yet safe from injection.
I will also endorse the notion of using DBIx::Class ... or SQL::Abstract if you have a lot of existing code to deal with. They are solid and helpful.