我有以下查询
"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 ... FROM ... ) 我怀疑它不是理解are子句的去向或者$ v1不是列的名称而是实际值,但是有人知道如何解决这个问题(t1只有两列ints c2是一个int而c3是也是一个int)。
此代码的最终目标是基本执行以下操作(在伪代码中)
$insertv1=$v1
$insertv2= select c2 from t2 where c3=$v2
query = insert INTO t1 VALUES ($insertv1,$insertv2)
请注意,对值$ v1和$ v2进行反参数化可以解决问题,但只是参数化其中一个会导致问题。
答案 0 :(得分:1)
正如past所说,您不能将列名作为参数传递给OleDB SQL查询。参数仅适用于值,不适用于表名或列名。
正如failed passed attempts在评论中提到的那样,这并不安全 - 因为这可能是危险的。主要问题是表名是否来自用户输入 - 如果是,则会在应用程序中留下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.
编辑:由于这不是你所要求的,我将解释我对你的伪代码的意思:
$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 :
在聊天中,我们进一步研究了compliation错误,因为参数化不同的值有一些奇怪的行为。我问过$v1
和$v2
中的值是什么,我们认为nvarchar
中的v2
被误解为伪名称。这也解释了为什么SQL有效但C#失败了;事实上,错误是OleDB在名称中的代表。解决方案很简单 - 在'
之前和之后添加v2,
标记,这将导致它被读取为非伪字符串,如下所示:"INSERT INTO t1 select $v1,c2 FROM t2 WHERE c3= '$v2'"
。