параметризованная вставка половины данных, выбранных из другой полуконстанты столбца, дает недопустимый псевдостолбец

У меня есть следующий запрос

     "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 ... ) Я подозреваю, что он не понимает, куда идет предложение 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 решает проблему, но просто параметризация одного из двух вызывает проблему.


person Thijser    schedule 14.08.2015    source источник
comment
Вы не можете параметризовать имена столбцов. Вы можете только параметризовать свои значения.   -  person Soner Gönül    schedule 14.08.2015
comment
То, что я пытаюсь сделать, это вставить параметризованное значение, сделав его частью оператора select, похожего на то, как вы можете сделать SELECT имя столбца, некоторую константу FROM table. См., например, ответ @travis в связанном вопросе.   -  person Thijser    schedule 14.08.2015


Ответы (1)


Как говорится в прошлом, вы не можете передать столбец name в SQL-запрос OleDB в качестве параметра. Параметры только для значений, а не имен таблиц или столбцов.

Как упоминают в своих комментариях неудачные пройденные попытки, это небезопасно - так как это может быть опасно. Основная проблема заключается в том, что если имя таблицы получено из пользовательского ввода - если это так, это оставит уязвимость 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:

В чате мы посмотрели дальше на ошибку компиляции, так как было какое-то странное поведение с параметризацией разных значений. Я спросил вас, какие значения находятся в $v1 и $v2, и мы выяснили, что nvarchar в v2 неверно интерпретируется как псевдоимя. Это также объясняет, почему SQL работает, а C# не работает; на самом деле ошибка заключалась в том, как OleDB интерпретировала имя. Решение простое — добавьте метки ' до и после v2,, что приведет к тому, что оно будет прочитано как непсевдострока, например: "INSERT INTO t1 select $v1,c2 FROM t2 WHERE c3= '$v2'".

person A. Abramov    schedule 14.08.2015
comment
Проблема в этом случае заключается в том, что tableName (v1) на самом деле не имя столбца или таблицы, а переменная, которую я хочу иметь в выводе моего оператора select. - person Thijser; 14.08.2015
comment
@Thijser Я думаю, что это можно сделать, но прежде чем мы приступим к этому - я не уверен, что на самом деле это будет лучше, поскольку эти два запроса довольно разные. запустите оба этих запроса в своей базе данных и измерьте разницу во времени. Если он есть, я помогу вам преобразовать его в один запрос. - person A. Abramov; 14.08.2015
comment
@Thijser Кстати, основная проблема с вашим кодом заключается в том, что вы пытаетесь заменить предыдущее дополнение другим, а OleDBQuery работает иначе. Если бы вы зациклили весь процесс создания запроса (начиная с первого вложения) и каждый раз переопределяли деклерацию cmd, все должно работать нормально. Если вы хотите, чтобы я отредактировал свой ответ и добавил пример, просто попросите об этом :) - person A. Abramov; 14.08.2015
comment
Вы имеете в виду, что этот код не приведет к вставке нескольких новых значений? Я основал эту конструкцию на ответе здесь: stackoverflow.com/questions/12426300/ будет ли этот ответ неверным? Что бы вы назвали допустимой конструкцией для добавления нескольких значений таким образом? - person Thijser; 14.08.2015
comment
@Thijser Я собирался написать, что тот факт, что вы используете $v2 как @column, имеет значение, поскольку это еще один параметр, основанный на запросе, но на самом деле это было бы неправильно. Тогда это довольно странно - понятия не имею, почему ваш код не работает. Я протестировал свое решение на Northwind, получил доступ к базе данных 2010 года, и все в порядке. - person A. Abramov; 14.08.2015
comment
Так что это работает, если выполняется непосредственно в базе данных, но не выполняется через код С#? Или это просто не работает против sql-сервера? Кажется, это работает, если я использую непараметризованное значение для $v2 (например, 1), но тогда мы будем работать с непараметризованными значениями. - person Thijser; 14.08.2015
comment
@Thijser SQL-часть того, что я написал, работает на Northwind, как и ваша. Часть C# моего (отдельного) решения работает на Northwind, а вы утверждаете, что ваша не работает с вашей базой данных. Так что, если с вашим соединением что-то не так, возможно, есть ошибка в части С#, но я не могу понять это... - person A. Abramov; 14.08.2015
comment
Обратите внимание, что параметризация значения $v2 также вызывает ту же странную проблему. - person Thijser; 14.08.2015
comment
Давайте продолжим обсуждение в чате. - person A. Abramov; 14.08.2015