Sql server 使用sp_executesql使用自动增量更新表

Sql server 使用sp_executesql使用自动增量更新表,sql-server,tsql,dynamic-sql,Sql Server,Tsql,Dynamic Sql,我试图将这个简单的sql语句转换为sp_executesql,因为表名是可变的: UPDATE @TableName SET @i = RowNumber = @i + 1 这就是我想到的,但它在执行时给出了一个错误:必须声明表变量@TableName DECLARE @i AS INT ; SET @i = 0 ; DECLARE @SQLString NVARCHAR(500); DECLARE @ParmDefinition NVARCHAR(500); SET @SQLString

我试图将这个简单的sql语句转换为sp_executesql,因为表名是可变的:

UPDATE @TableName SET @i = RowNumber = @i + 1
这就是我想到的,但它在执行时给出了一个错误:必须声明表变量@TableName

DECLARE @i AS INT ;
SET @i = 0 ;
DECLARE @SQLString NVARCHAR(500);
DECLARE @ParmDefinition NVARCHAR(500);

SET @SQLString = N'UPDATE @TableName SET @i = RowNumber = @i + 1';
SET @ParmDefinition = N'@TableName VARCHAR(30), @i  TINYINT';

EXECUTE sp_executesql @SQLString, @ParmDefinition, @TableName=@TableName, @i = @i;
我正在尝试向现有表中添加另一列,这基本上类似于ID递增的标识列。表已经有一个ID列,我无法删除/更新它

请不要推荐标识栏


我在一个存储过程中执行这个查询,@TableName作为一个参数进入,并且是一个临时表的名称,所以我希望它在所有内部作用域中都可用,包括sp_exec。

当您将变量放在引号中时,它不会被计算,只是文本。 另外,最好显式写入要更新的列名

DECLARE @i AS INT ; 
SET @i = 0 ;
DECLARE @ColumnName as NVARCHAR(1000) ='MyColumn'
DECLARE @TableName as NVARCHAR(1000) = 'MyTable'
DECLARE @SQLString NVARCHAR(500);

SET @SQLString = N'UPDATE ' +  @TableName + ' SET ' + @ColumnName  + ' = ' + @i + '+1';

EXECUTE sp_executesql @SQLString;

结论-您应该在单个变量中连接整个更新字符串,然后通过sp_executesql运行它。首先,表名不能参数化。我建议改为使用QUOTENAME作为字符串文本进行保护。如果同时提供了schema和tablename,则必须将它们作为单独的参数提供并引用

第二:SET@i=RowNumber=@i+1'看起来是一种使用连续整数迭代更改RowNumber的方法。为此,我将使用带有行号的updatetable cte,并在此处添加起始位置@I

第三:假设表中已经存在列RowNumber。如果不是动态的,则必须首先发出ALTERTABLE ADD RowNumber INT

完整代码:

DECLARE @TableName SYSNAME = 't';

DECLARE @i AS INT = 0 ;
DECLARE @SQLString NVARCHAR(MAX);
DECLARE @ParmDefinition NVARCHAR(MAX);

SET @SQLString = 
N'WITH cte AS (SELECT *, rn = ROW_NUMBER() OVER(ORDER BY (SELECT 1)) FROM <TableName>)
  UPDATE cte SET RowNumber = @i + rn';
SET @ParmDefinition = N'@i  TINYINT';

SET @SQLString = REPLACE(@SQLString, '<TableName>', QUOTENAME(@TableName));

PRINT @SQLString;  -- debug

EXECUTE sp_executesql @SQLString, @ParmDefinition, @i = @i;
使用CONCAT函数而不是+运算符

更新表列

通过使用嵌套子查询,可以更新表的列

SET @SQL=CONCAT('UPDATE X',' SET X.',@Col_NAME,' = X.ID FROM (SELECT ',@Col_NAME,', ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) as ID FROM ',QUOTENAME(@TABLE_NAME),' ) X')

SELECT @SQL

EXEC (@SQL)

下面的代码演示如何处理各种参数并从动态SQL返回值

代码不使用每个计数器的表,而是使用一个计数器表,并为每个计数器分配名称CounterName。将显示递增表中不同计数器的值

计数器不传递要递增的值,例如OP的@i,而是维护自己的值,每次执行时返回递增的值。如果需要传入要递增的值,那么可以很容易地更改更新以使用@pCounter的值作为源,并且可以使用一个参数传入并返回该值

表名嵌入在update语句中,因此不能作为参数传递。在汇编语句时,必须将其插入语句中。QuoteName用于处理问题名称,例如此处的空格,并提供一些针对SQL注入的保护

代码可以在以下位置进行测试。在撰写本文时,SQLServer2019在DBFIDLE上似乎不起作用。因此使用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;

您使用的是哪种数据库管理系统?这段代码非常特定于产品。好吧,它是的半个副本,并且没有将@i声明为输出。旁白:缺少where子句,更新将应用于所有行。如果有多行,您希望返回哪个行号?您可能还想看看。这也不是运行动态语句的方式。您需要使用QUOTENAME安全地注入该值。如果您可以像这样参数化一个表名,您根本不需要动态SQL。@jarlh我正在使用SQL Server 2016
-- 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;