无法将值插入 SQL table,值被误认为是列

Trouble inserting values into SQL table, values get mistaken for columns

我不断收到异常:

"Invalid Column Name, 'IBM'."

错误发生在:"' + @ticker + '",即使在 VALUES 中声明了 @ticker。我怀疑错误可能发生在查询的其他位置,但我对 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 注入的违规行为,并解释了 sp_executesql.

的必要性

担忧

  1. 您不能使用参数附加 table 名称和列名称的值。相反,需要 字符串连接 ,尽管这将 导致开放的 SQL 注入攻击 (示例:Bobby Tables)。因此,请确保您已 tableName 或字符串连接部分进行了足够的验证

  2. 您不需要为查询中的参数附加单引号 '。当SQLCommand根据参数类型追加SQLParameter值进行查询时会自动完成。

  3. 我看不出有必要使用 EXEC sp_executesql。虽然您可以 straight-way 执行 INSERT 查询。

  4. 在 Whosebug 社区中,通常会建议使用 SqlCommand.Add("@Name", SqlDbType).Value 并指定参数类型而不是 SqlCommand.AddWithValue()。参考Can we stop using AddWithValue() already?.

总之,您的 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;

您应该将包含数据(而不是列和 table 名称)的参数一直传递到 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;