参数化插入的数据一半是从其他列中选择的,一半常量给出无效的伪列

本文关键字:一半 常量 无效 选择 其他 插入 数据 参数 | 更新日期: 2023-09-27 18:07:50

我有以下查询

     "INSERT INTO t1 select $v1,c2 FROM t2 WHERE c3= $v2";

作为

执行
    SqlCommand cmd= new SqlCommand(query, conn);
    cmd.Parameters.AddWithValue("$v2", data);
    foreach (string value in list)
    {
        cmd.Parameters.AddWithValue("$v1", value);
        cmd.ExecuteNonQuery();
    }

但是这会导致错误:

An unhandled exception of type 'System.Data.SqlClient.SqlException' occurred in System.Data.dll
Additional information: Invalid pseudocolumn "$v1".

这是基于以下问题:SQL Insert into…values (SELECT…)来自……我怀疑它不理解where were子句去或者$v1不是列的名称,而是一个实际的值,但是有人知道如何修复这个(t1只有2列,都是int, c2是一个int和c3也是一个int)。

这段代码的最终目标基本上是完成以下(在伪代码中)

$insertv1=$v1
$insertv2= select c2 from t2 where c3=$v2 
query = insert INTO t1 VALUES ($insertv1,$insertv2)

注意,对$v1和$v2去参数化可以解决问题,但只参数化其中一个就会导致问题。

参数化插入的数据一半是从其他列中选择的,一半常量给出无效的伪列

如前所述,您不能将列名作为参数传递给OleDB SQL查询。参数只能用于值,不能用于表名或列名。

正如他们评论中提到的失败的通过尝试,这是不安全的——因为这可能是危险的。主要问题是,如果表名来自用户输入—如果是这样,它将在应用程序中留下SQL注入漏洞。因此,如果它确实来自用户输入,您需要保护自己。最简单的方法是确保表名是有效的表名、附加表名或查询名——要做到这一点,可以使用如下查询:

SELECT Name FROM Myssobjects Where Type in (1,5,6,)

此查询将返回数据库中表的有效值,您可以将其与获得的输入进行比较。请注意,您仍然应该谨慎使用此选项,因为大多数情况下您不会希望让用户访问整个数据库。

无论如何,如果你很好地保护了自己,并确保你不再容易受到攻击,这样做的方法是动态地为查询创建字符串——就像这样:
string tableName = value;
string query =  "INSERT INTO t1 select "+tableName+",c2 FROM t2 WHERE c3= $v2";
SqlCommand cmd= new SqlCommand(query, conn);
cmd.Parameters.AddWithValue("$v2", data);
// etc. - instead of the AddWithValue(), you add it to the string.

EDIT:由于这不是您所要求的,因此我将解释与您的伪代码相关的意思:

$insertv1=$v1
$insertv2= select c2 from t2 where c3=$v2 
query = insert INTO t1 VALUES ($insertv1,$insertv2)

实际上需要分离两个查询,因此转换为:

string v1="Your value here.",v2="Your second value here.",v3="";
//first : get v2.
SqlCommand cmd= new SqlCommand("select c2 from t2 where c3=$v2", conn); // using the v2 query you stated.
cmd.Parameters.AddWithValue("$v2", v2);
cmd.ExecuteNonQuery();
// put the value from the query into v3
// then, make the entire, bigger query
string finalQuery =  "INSERT INTO t1 VALUES($v1,$v2)";
SqlCommand cmd2= new SqlCommand(finalQuery, conn);
cmd2.Parameters.AddWithValue("$v1", v1);
cmd2.Parameters.AddWithValue("$v2", v3);
cmd2.ExecuteNonQuery();

注意,对$v1和$v2去参数化可以解决问题,但只参数化其中一个就会导致问题。

编辑2 :

在chat中,我们进一步研究了复杂性错误,因为参数化不同的值有一些奇怪的行为。我问你$v1$v2的值是什么,我们认为v2中的nvarchar被误解为假名。这也解释了为什么SQL可以工作,但c#失败;错误实际上是OleDB解释名称的方式。解决方案很简单-在v2,之前和之后添加'标记,这将导致它被读取为非伪字符串,如:"INSERT INTO t1 select $v1,c2 FROM t2 WHERE c3= '$v2'" .