You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何更新DataGridView并实现带外键关联的tblChild表批量增删改操作

实现DataGridView中tblChild表的批量增删改操作

你的现有代码思路方向是对的,但存在几个关键问题:只检查了第一行的ChildID来判断更新/新增逻辑,完全没处理删除操作,SQL语句也有可以简化的地方,而且没考虑DataGridView的空行(IsNewRow)。下面给你梳理正确的实现方案:

核心思路

我们可以通过判断DataGridView行的状态(新增、已修改、已删除),结合ChildID是否存在来分别执行对应的SQL操作:

  • 新增行:ChildID为空(或行是IsNewRow),执行INSERT
  • 修改行:ChildID存在且行内容有修改,执行UPDATE
  • 删除行:用户通过DataGridView删除的行,执行DELETE

优化后的完整代码

private void btnSave_Click(object sender, EventArgs e)
{
    // 先获取当前关联的FileID,确保不为空
    if (string.IsNullOrWhiteSpace(txtID.Text))
    {
        MessageBox.Show("请先选择关联的文件!");
        return;
    }
    int fileID = int.Parse(txtID.Text.Trim());

    // 维护要删除的ChildID列表(因为DataGridView删除的行会进入Deleted状态)
    List<int> deletedChildIDs = new List<int>();

    // 遍历DataGridView的所有行,跳过空行
    foreach (DataGridViewRow row in dgv.Rows)
    {
        if (row.IsNewRow) continue; // 跳过新增的空行

        // 获取当前行的ChildID,判断是新增还是修改
        object childIDValue = row.Cells[0].Value;
        bool isNewRow = childIDValue == null || string.IsNullOrWhiteSpace(childIDValue.ToString());

        if (row.State == DataGridViewRowState.Deleted)
        {
            // 处理删除:如果是已删除的行,记录ChildID
            if (!isNewRow)
            {
                deletedChildIDs.Add(int.Parse(childIDValue.ToString()));
            }
            continue;
        }

        // 获取行数据
        string childName = row.Cells[1].Value?.ToString() ?? string.Empty;
        DateTime? birthDate = row.Cells[2].Value as DateTime?;

        // 确保必填字段不为空(根据你的业务需求调整)
        if (string.IsNullOrWhiteSpace(childName))
        {
            MessageBox.Show($"第{row.Index+1}行的姓名不能为空!");
            return;
        }

        using (SqlConnection con = new SqlConnection(ClsDB.ConnectionString)) // 建议用连接字符串而非全局连接对象
        {
            con.Open();
            if (isNewRow)
            {
                // 新增操作
                string insertSql = @"INSERT INTO tblChild (ChildName, ChildBirthDate, fkFileID) 
                                     VALUES (@ChildName, @ChildBirthDate, @FileID)";
                using (SqlCommand cmd = new SqlCommand(insertSql, con))
                {
                    cmd.Parameters.AddWithValue("@ChildName", childName);
                    cmd.Parameters.AddWithValue("@ChildBirthDate", birthDate ?? DBNull.Value);
                    cmd.Parameters.AddWithValue("@FileID", fileID);
                    cmd.ExecuteNonQuery();
                }
            }
            else
            {
                // 更新操作
                int childID = int.Parse(childIDValue.ToString());
                string updateSql = @"UPDATE tblChild 
                                     SET ChildName = @ChildName, ChildBirthDate = @ChildBirthDate, fkFileID = @FileID
                                     WHERE ChildID = @ChildID";
                using (SqlCommand cmd = new SqlCommand(updateSql, con))
                {
                    cmd.Parameters.AddWithValue("@ChildID", childID);
                    cmd.Parameters.AddWithValue("@ChildName", childName);
                    cmd.Parameters.AddWithValue("@ChildBirthDate", birthDate ?? DBNull.Value);
                    cmd.Parameters.AddWithValue("@FileID", fileID);
                    cmd.ExecuteNonQuery();
                }
            }
        }
    }

    // 批量处理删除操作
    if (deletedChildIDs.Count > 0)
    {
        using (SqlConnection con = new SqlConnection(ClsDB.ConnectionString))
        {
            con.Open();
            // 用IN语句批量删除,注意如果ID数量很多需要调整(比如用表值参数)
            string deleteSql = $"DELETE FROM tblChild WHERE ChildID IN ({string.Join(",", deletedChildIDs)})";
            using (SqlCommand cmd = new SqlCommand(deleteSql, con))
            {
                cmd.ExecuteNonQuery();
            }
        }
    }

    MessageBox.Show("保存成功!");
    // 刷新DataGridView数据(可选,根据你的数据绑定方式调整)
    LoadChildData();
}

// 可选:加载tblChild数据到DataGridView的方法示例
private void LoadChildData()
{
    if (string.IsNullOrWhiteSpace(txtID.Text)) return;
    int fileID = int.Parse(txtID.Text.Trim());
    using (SqlConnection con = new SqlConnection(ClsDB.ConnectionString))
    {
        string sql = "SELECT ChildID, ChildName, ChildBirthDate FROM tblChild WHERE fkFileID = @FileID";
        SqlDataAdapter sda = new SqlDataAdapter(sql, con);
        sda.SelectCommand.Parameters.AddWithValue("@FileID", fileID);
        DataTable dt = new DataTable();
        sda.Fill(dt);
        dgv.DataSource = dt;
    }
}

关键改进点说明

  1. 处理删除操作:通过捕获DataGridViewRowState.Deleted的行,收集要删除的ChildID,最后批量删除,效率更高。
  2. 简化SQL语句:原代码中fkFileID = (SELECT FileID FROM tblFile WHERE FileID = @FileID)完全可以直接写成fkFileID = @FileID,因为@FileID就是关联的FileID,没必要子查询。
  3. 避免全局连接对象:建议使用using包裹SqlConnection,确保连接自动释放,避免连接泄漏问题。
  4. 跳过空行:判断row.IsNewRow跳过DataGridView末尾的空行,避免插入无效数据。
  5. 参数化查询:全程使用参数化查询,防止SQL注入风险。

额外注意事项

  • 确保DataGridView的AllowUserToDeleteRows属性设置为true,这样用户可以通过右键或Delete键删除行。
  • 如果你的ChildID是自增主键,新增行的时候不需要手动赋值,数据库会自动生成。
  • 如果需要处理大量数据,批量删除时建议使用表值参数代替IN语句,避免SQL语句过长的问题。

内容的提问来源于stack exchange,提问作者User_Michel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 20:52:34