The following code demonstrates how to handle the various parameters and return a value from the dynamic SQL.
Rather than using a table-per-counter, the code uses a single table of counters with a name (CounterName) assigned to each. Incrementing the different counters within the table is shown.
Instead of passing in a value to be incremented, e.g. the OP's @i, the counters maintain their own values and the incremented value is returned on each execution. If passing in the value to be incremented is a requirement then the update can be easily changed to use the value of @pCounter as a source and the value can be passed in and returned using one parameter.
The table name is embedded in the update statement and, as such, cannot be passed as a parameter. It must be inserted into the statement while it is being assembled. QuoteName is used to handle problem names, e.g. Space Here, and offer some protection against SQL injection.
The code can be tested at db<>fiddle. (At the time of this writing SQL Server 2019 appears to be non-functional at db<>fiddle. Hence the use of SQL Server 2017.)
-- Sample data.
-- The Counters table has a row for each counter to be kept, rather than a table per counter.
create table Counters ( CounterId Int Identity, Counter Int Default 0, CounterName NVarChar(64) );
insert into Counters ( CounterName ) values ( 'Shoes' ), ( 'Widgets' );
select * from Counters;
-- Build the query.
declare @Counter as Int = 0;
declare @SQL as NVarChar(500);
declare @ParameterDefinitions as NVarChar(100);
declare @TableName as SysName = N'Counters';
declare @CounterName as NVarChar(64) = 'Widgets';
-- Note that the table name needs to be substituted into the query here, not passed as a parameter.
-- Using different names for the parameters within the query and the variables being passed makes
-- things a little clearer.
set @SQL =
N'update ' + QuoteName( @TableName ) +
' set @pCounter = Counter += 1' +
' where CounterName = @pCounterName;';
set @ParameterDefinitions = N'@pCounterName NVarChar(64), @pCounter Int Output';
-- Execute the query.
-- Note that output parameters must be specified here as well as in the parameter definitions.
execute sp_ExecuteSQL @SQL, @ParameterDefinitions,
@pCounterName = @CounterName, @pCounter = @Counter output;
select @CounterName as [@CounterName], @Counter as [@Counter];
select * from Counters;
execute sp_ExecuteSQL @SQL, @ParameterDefinitions,
@pCounterName = @CounterName, @pCounter = @Counter output;
select @CounterName as [@CounterName], @Counter as [@Counter];
select * from Counters;
-- Try a different counter.
set @CounterName = N'Shoes';
execute sp_ExecuteSQL @SQL, @ParameterDefinitions,
@pCounterName = @CounterName, @pCounter = @Counter output;
select @CounterName as [@CounterName], @Counter as [@Counter];
select * from Counters;
-- Houseclean.
drop table Counters;