Я продолжаю получать исключение, которое гласит:
"Invalid Column Name, 'IBM'."
Ошибка возникает по адресу: "' + @ticker + '"
, хотя @ticker
объявлено в VALUES
. Я подозреваю, что ошибка может происходить в какой-то другой точке запроса, но я новичок в SQL/T-SQL, поэтому не знаю, как выяснить, где именно.
private string InsertRecord(Indicator indicator)
{
try
{
if (!CheckIfColumnExists(indicator.GetType().Name))
{
AddColumn(indicator.GetType().Name, SqlDbType.Real);
}
const string query = @"
DECLARE @sql nvarchar(max) = '
INSERT INTO ' + QUOTENAME(@tableName) + '(
' + QUOTENAME(@indicator) + ', date, ticker)
VALUES(' + @indicatorValue + ', ' + @date + ', ' + @ticker + ')
';
EXEC sp_executesql @sql;
";
//checking if the record is already there
if (!CheckIfRecordExists(indicator))
{
using (SqlConnection conn = new SqlConnection(this.connectionstring))
{
conn.Open();
SqlCommand cmd = new SqlCommand(query , conn);
cmd.Parameters.AddWithValue("@tableName", tableName);
cmd.Parameters.AddWithValue("@indicator", indicator.GetType().Name);
cmd.Parameters.AddWithValue("@indicatorValue", indicator.Value.ToString());
cmd.Parameters.AddWithValue("@date", indicator.Date.ToString("yyyy-MM-dd"));
cmd.Parameters.AddWithValue("@ticker", indicator.Ticker);
var result = cmd.ExecuteNonQuery();
return "New Record Inserted";
}
}
else
{
return "Record Already Exists";
}
}
catch
{
return "Failure Inserting New Record";
}
}
Обновлено: я принимаю ответ CharlieFace, потому что он позволяет избежать нарушений через SQL Injection и объясняет необходимость sp_executesql.
@YongShun Indicator
— это класс, который расширяет несколько подклассов, поэтому indicator.GetType().Name
будет меняться в зависимости от того, как объявлен индикатор, в данном случае строка читается как «SMA». Кроме того, спасибо за советы, я думаю, я просто использую конкатенацию.
@ŇɏssaPøngjǣrdenlarp Хорошо, спасибо, я их уберу. Спасибо и за совет.
Обеспокоенность
Вы не можете использовать параметры для добавления значений для имени таблицы и имени столбца. Вместо этого требуется конкатенация строк, хотя это будет привести к открытой атаке SQL Injection (пример: Бобби Столы). Следовательно, убедитесь, что у вас есть сделано достаточно проверки для tableName
или части конкатенации строк.
Вам не нужно добавлять одинарные кавычки '
для параметров в запросе. Это будет сделано автоматически, когда SQLCommand
добавит значение SQLParameter
к запросу в соответствии с типом параметра.
Не вижу необходимости использовать EXEC sp_executesql
. Хотя вы можете просто сразу выполнить запрос INSERT
.
В сообществе StackOverflow обычно предлагается использовать SqlCommand.Add("@Name", SqlDbType).Value
и указывать тип параметра вместо SqlCommand.AddWithValue()
. См. Можем ли мы уже отказаться от использования AddWithValue()?.
В заключение, ваш SqlCommand
должен быть таким:
string query = @"
INSERT INTO " + tableName +
"(" + indicator.GetType().Name + ", date, ticker)" +
" VALUES (@indicatorValue, @date, @ticker)";
SqlCommand cmd = new SqlCommand(query , conn);
cmd.Parameters.Add("@indicatorValue", SqlDbType.NVarchar).Value = indicator.Value.ToString();
cmd.Parameters.Add("@date", SqlDbType.NVarchar).Value = indicator.Date.ToString("yyyy-MM-dd");
cmd.Parameters.Add("@ticker", SqlDbType.NVarchar).Value = indicator.Ticker;
Есть является причина использовать sp_executesql
: чтобы использовать QUOTENAME
. Вы не используете его, и поэтому могут возникнуть проблемы с инъекцией. Также вы должны передавать длины параметров, а не только типы. А у значений параметров еще есть ToString()
чего не должно быть
Вы должны передать параметры, которые содержат данные (в отличие от имен столбцов и таблиц), вплоть до sp_executesql
const string query = @"
DECLARE @sql nvarchar(max) = '
INSERT INTO ' + QUOTENAME(@tableName) + '(
' + QUOTENAME(@indicator) + ', date, ticker)
VALUES(@indicatorValue, @date, @ticker)
';
EXEC sp_executesql @sql,
N'@indicatorValue nvarchar(100), @date date, @ticker nvarchar(100)',
@indicatorValue,
@date,
@ticker;
";
Вы также должны передавать параметры как их реальные значения (дата, целое число), а не ToString
. Также явно объявляйте типы и длины параметров.
// table and column name should be NVARCHAR(128)
cmd.Parameters.Add("@tableName", SqlDbType.NVarchar, 128).Value = tableName;
cmd.Parameters.Add("@indicator", SqlDbType.NVarchar, 128).Value = indicator.GetType().Name;
cmd.Parameters.Add("@indicatorValue", SqlDbType.NVarchar, 100).Value = indicator.Value;
cmd.Parameters.Add("@date", SqlDbType.Date).Value = indicator.Date;
cmd.Parameters.Add("@ticker", SqlDbType.NVarchar, 100).Value = indicator.Ticker;
была небольшая синтаксическая ошибка в : N'@indicatorValue nvarchar(100), @date date, @ticker nvarchar(100);,
, должно быть: N'@indicatorValue nvarchar(100), @date date, @ticker nvarchar(100)',
Кроме того, вы не добавляете галочки в заполнители параметров (
'@placeholder'
). В общем, вместо того, чтобы создавать сложные машины, такие как код для вставки в любую таблицу, начните с простого, прямого кода, пока у вас не будет нескольких лет опыта.