用动态表名查询

时间:2019-06-19 14:13:35

标签: c# sql ado.net

我想在参数化语句中更改动态SQL语句,但是在查询中表名称是动态的。因此,我尝试以这种方式来修饰整个语句,就像在this link中newCRMsupport的答案中看到的那样:

command.CommandType = CommandType.Text;

string sqlStr = " DECLARE @sqlSubstr nvarchar(max) SET @sqlSubstr = N'UPDATE quotename(@tempTable) = SET @flag = 1 WHERE @tempCol = @tempColVal' EXECUTE sp_executesql @sqlSubstr";
command.CommandText = sqlStr;
command.Parameters.AddWithValue("@tempTable", TemporaryTableName);
command.Parameters.AddWithValue("@flag", flagToUpdate);
command.Parameters.AddWithValue("@tempCol", ImportRegister.TemporaryTableKeyColumn);
command.Parameters.AddWithValue("@tempColVal", sourceRow[ImportRegister.TemporaryTableKeyColumn]);
command.ExecuteNonQuery();

但是当我运行它时,我有一个异常。 “必须声明calar变量@tempTable”,但我无法弄清缺少的内容。 谢谢

1 个答案:

答案 0 :(得分:1)

像这样在数据库设计或应用程序设计上使用动态SQL都表明设计不佳。

但是,假设无法更改设计,那么当前SQL语句的问题在于您实际上并未使用动态SQL。
一个动态SQL看起来像这样:

DECLARE @sqlSubstr nvarchar(max) = N'UPDATE '+ quotename(@tempTable) +N' 
    SET '+ quotename(@flag) +N' = 1 
    WHERE '+ quotename(@tempCol) +' = @tempColVal;

EXECUTE sp_executesql @sqlSubstr, N'@tempColVal varchar(2)', @tempColVal;

请注意,表名和列名的参数被连接到代表您正在执行的SQL的字符串中。
另外,请注意,我还为列名添加了quotename

但是,我不确定这是否提供了针对SQL注入攻击的完全可靠的保护。 quotename的使用确实提供了一些保护,但是我很确定可以克服这个问题。
为了真正保护自己,您必须将所有标识符列入白名单-因此,您首先需要查询information_schema.columns以确保一切安全。

只有这样做,您才能确保代码是SQL Injection安全的。 在此期间,you really should stop using AddWithValue already.

这是您代码的修订版:

string sqlStr = @"DECLARE @sqlSubstr nvarchar(max) = 
    N'UPDATE '+ quotename(@tempTable) +
    N' SET '+ quotename(@flag) +
    N' = 1 WHERE '+ quotename(@tempCol) +' = @tempColVal' 
    IF EXISTS(
        -- make sure both columns exists in the table
        SELECT 1
        FROM Information_schema.Columns
        WHERE Table_Name = @tempTable
        AND Column_Name IN(@flag, @tempCol)
        HAVING COUNT(DISTINCT Column_Name) = 2 
    )
    EXECUTE sp_executesql @sqlSubstr N'@tempColVal nvarchar' @tempColVal"; -- I had to guess the data type

command.CommandText = sqlStr;
command.Parameters.Add("@tempTable", SqlDbType.NVarChar).Value = TemporaryTableName;
command.Parameters.Add("@flag", SqlDbType.NVarChar).Value = flagToUpdate;
command.Parameters.Add("@tempCol", SqlDbType.NVarChar).Value = ImportRegister.TemporaryTableKeyColumn;
command.Parameters.Add("@tempColVal", SqlDbType.NVarChar).Value = sourceRow[ImportRegister.TemporaryTableKeyColumn]);
command.ExecuteNonQuery();