C# 中的 SQL Server 更新语句

Posted

技术标签:

【中文标题】C# 中的 SQL Server 更新语句【英文标题】:SQL Server Update statement in C# 【发布时间】:2021-12-03 00:51:20 【问题描述】:

我有表 SelectedType 的列 type_1 ... type_2_3 的数据类型为 bit

P_ID   type_1 type_1_1 type_1_2 type_1_3 type_2 type_2_1 type_2_2 type_2_3

 1        0      1         1       0       0       1        0        1       
 2        1      0         1       0       0       1        0        0
 3        1      0         1       0       0       1        0        0
 4        0      0         0       1       1       1        1        1
...and so on 

和 JSON 数组

[
  "value": "type_1_1", "value": "type_2_1", "value": "type_2_3"
]

如何使用 for 循环更新 SQL Server 表,如果 "value": "type_1_1" 等于表列名 type_1_1 则将值设置为 1,或者如果值为 1 则设置为 0?

这是我目前正在尝试的:

public bool UpdatePredictSubGoalType(int id, string _selectedTypes)

        string a = "[]";
        string item = _selectedTypes;

        if (!item.Contains('[') && !item.Contains(']'))
        
            string c = a.Insert(1, _selectedTypes);
            item = c;
        

        bool res = false;

        JArray Ja = JArray.Parse(item);
        string sqlstr = "";

        try
        
            using (conn = new SqlConnection(CommonDA.GetConnectionString()))
            
                conn.Open();
                trans = conn.BeginTransaction();

                try
                
                    for (int i = 0; i <= Ja.Count - 1; i++)
                    
                        string x = Ja[i]["value"].ToString();

                        SqlCommand cmd = conn.CreateCommand();
                        sqlstr = @"Update SelectedType set "+ x +" = (0 or 1, i don't know how to do) where id = @id AND " + x + " = @" + x;
                        cmd.CommandText = sqlstr;
                        cmd.Transaction = trans;
                        cmd.Parameters.AddWithValue("@id", id);                            

                        cmd.ExecuteNonQuery();

                        trans.Commit();
                        conn.Close();

                        res = true;
                    
                
                catch (Exception Err)
                
                    trans.Rollback();
                    CommonLB.SystemError(Err.Message);
                    CommonLB.SystemError("Data", "SQL:" + sqlstr);
                
            
        
        catch (Exception Err)
        
            throw new ApplicationException(Err.Message, Err);
        

        return res;
    

【问题讨论】:

如果我正确理解您的要求,请将值设置为1 - currentvalue 您从哪里获得价值? json 只有列名,但没有这些列的值 @Nonik 我假设“存在列 = 1,不存在列 = 0” 我还假设 json 包含一个我们看不到的 ID 我明白了,所以列将始终是静态的,在这种情况下,创建一个包含所有列的数组,根据缺失的列确定缺少哪些列,设置您的值 【参考方案1】:

您可以将整个 JSON 数组传递给 SQL,然后让 SQL Server 将其分解。

使用OPENJSON 显示单独的行 将其转回单行,每列有一个 1 或 0 使用ISNULLNULLIF 的组合来保持相同的值 如果你想覆盖所有列,那么你可以简单地做type_1 = j.type_1
UPDATE st
SET
  type_1   = IIF(j.type_1   = 1, 1, st.type_1  ),
  type_1_1 = IIF(j.type_1_1 = 1, 1, st.type_1_1),
  type_1_2 = IIF(j.type_1_2 = 1, 1, st.type_1_2),
  type_1_3 = IIF(j.type_1_3 = 1, 1, st.type_1_3),
  type_2   = IIF(j.type_2   = 1, 1, st.type_2  ),
  type_2_1 = IIF(j.type_2_1 = 1, 1, st.type_2_1),
  type_2_2 = IIF(j.type_2_2 = 1, 1, st.type_2_2),
  type_2_3 = IIF(j.type_2_3 = 1, 1, st.type_2_3)
FROM SelectedType st
CROSS APPLY (
    SELECT *
    FROM OPENJSON(@json)
      WITH (value sysname) j
    PIVOT (COUNT(value) FOR value IN
        (type_1, type_1_1, type_1_2, type_1_3, type_2, type_2_1, type_2_2, type_2_3)
    ) pvt
) j
WHERE st.P_ID = @id;

那么你的应用代码就变得很简单了

public bool UpdatePredictSubGoalType(int id, string _selectedTypes)

    if (!item.StartsWith('['))
        _selectedTypes = '[' + _selectedTypes;
    if (!item.EndsWith(']'))
        _selectedTypes = _selectedTypes + ']';

    try
    
        const string query = @"
THE ABOVE QUERY
";
        using (var conn = new SqlConnection(CommonDA.GetConnectionString()))
        using (var cmd = new SqlCommand(query, conn))
        
            cmd.Parameters.Add("@id", SqlDbType.Int).Value = id;                            
            cmd.Parameters.Add("@json", SqlDbType.NVarChar, -1).Value = _selectedTypes;
            conn.Open();
            cmd.ExecuteNonQuery();
        
    
    catch (Exception Err)
    
        throw new ApplicationException(Err.Message, Err);
    

    return true;

【讨论】:

【参考方案2】:

假设我在 cmets 中提到的内容是正确的(值的存在定义为 1,缺失定义为 0)我认为,在设置 1 之前,您需要将所有内容设置为 0,或者收集你所有的列,所以 C# 可以计算出应该是 0..

类似这样的东西(安装 Dapper 以使连接上的 ExecuteAsync 成为“一件事”,或将其扩展为“长路”):

    using var conn = new SqlConnection(CommonDA.GetConnectionString()));

    var sql = "UPDATE t SET type_1=0, type_1_1=0, type_1_2=0, type_1_3=0, type_2=0, type_2_1=0, type_2_2=0, type_2_3=0 WHERE ID = @id";

    for (int i = 0; i <= Ja.Count - 1; i++)
      var c = Ja[i]["value"].ToString();

      if(!Regex.IsMatch(c, @"^type_\d(_\d)?$")
        continue;
      sql = sql.Replace($"c=0", $"c=1");
    

    await conn.ExecuteAsync(sql, new  id );

我们首先命名表中的每一列,并将其设置为type_x_y=0。然后对于 json 中存在的每个列名,我们将 SQL 的那部分更改为 type_x_y=1 并进行一些字符串替换

Regex 用于防止 json 供应商的 SQL 注入攻击

【讨论】:

【参考方案3】:

更改你的 sql 字符串:

str = $"Update SelectedType set [x] = CASE WHEN [x] = 0 then 1 else 0 end where id = @id";

【讨论】:

以上是关于C# 中的 SQL Server 更新语句的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server参数化SQL语句中的like和in查询的语法(C#)

SQL Server参数化SQL语句中的like和in查询的语法(C#)

SQL Server参数化SQL语句中的like和in查询的语法(C#)

我想使用 CASE 语句来更新 sql server 2005 中的一些记录

C# SQL 更新语句不更新

C# 中的 Sql Parser 用于语法检查 Oracle 语句