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

Golang操作MySQL用IN条件删除仅删一条数据的问题排查

问题

使用Go 1.16结合go-sql-driver/mysql v1.7.1驱动执行MySQL的DELETE语句,目标通过IN条件批量删除记录,但实际仅删除了一条数据。相关代码如下:

// Go版本声明
go 1.16
import (
    "database/sql"
    "errors"
    "fmt"
    "io"
    "log"
    "net/http"
    "strconv"
    "strings"
    "time"

    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"

    _ "github.com/go-sql-driver/mysql" //v1.7.1
)
func SelectQuery(action string, schema string) string {
    if action == "del" {
        switch schema {
        case "schema1":
            return "DELETE FROM `schema1` WHERE `id` IN (?)"
        default:
            panic("bad request")
        }
    }
    return "" // 补充原代码缺失的返回值
}
func DelAction(db *sql.DB, schema string, list []int) int64 {
    strList := []string{}
    for _, v := range list { // ID列表示例:[1, 2, 3]
        strList = append(strList, strconv.Itoa(v))
    }
    delQuery := SelectQuery("del", schema) // 得到SQL:"DELETE FROM `schema1` WHERE `id` IN (?)"
    param := strings.Join(strList, ",") // param值为:"1,2,3"
    delRes, err := db.Exec(delQuery, param)
    delCnt, err := delRes.RowsAffected()
    return delCnt // 实际仅删除1条记录
}

补充:待删除ID通过查询得到,代码示例:

res, err := db.Query(query) // 查询ID的SQL语句

list := []int{}
for res.Next() {
    u := &Logs{} // Logs结构体包含int类型的id字段
    if err := res.Scan(&u.id); err != nil {
        fmt.Println("scan error")
        panic(err.Error())
    } else {
        list = append(list, u.id)
    }
}
原因分析

你把多个ID拼接成单个字符串"1,2,3"作为参数传入SQL,但go-sql-driver/mysql会将这个字符串当作单个完整值处理。实际执行的SQL等价于:

DELETE FROM `schema1` WHERE `id` IN ('1,2,3')

MySQL会把字符串'1,2,3'隐式转换为整数1,因此只会匹配ID为1的记录,最终只删除一条数据。

解决方案

方法一:动态生成占位符(推荐,安全且性能优)

根据ID列表的长度,动态生成对应数量的?占位符,将每个ID作为独立参数传入,既能避免SQL注入,又能正常利用索引:

func DelAction(db *sql.DB, schema string, list []int) int64 {
    if len(list) == 0 {
        return 0 // 空列表直接返回,避免生成无效SQL
    }
    // 生成对应数量的占位符,比如3个ID生成"?, ?, ?"
    placeholders := strings.Repeat("?, ", len(list))
    placeholders = strings.TrimSuffix(placeholders, ", ")
    // 替换原SQL中的单个?为动态占位符
    delQuery := strings.Replace(SelectQuery("del", schema), "?", placeholders, 1)
    
    // 将int切片转为interface{}切片,作为Exec的参数
    args := make([]interface{}, len(list))
    for i, v := range list {
        args[i] = v
    }
    
    delRes, err := db.Exec(delQuery, args...)
    if err != nil {
        log.Fatal(err) // 根据业务需求调整错误处理逻辑
    }
    delCnt, err := delRes.RowsAffected()
    if err != nil {
        log.Fatal(err)
    }
    return delCnt
}

方法二:使用FIND_IN_SET函数(不推荐,仅作备选)

如果不想修改占位符,可以用MySQL的FIND_IN_SET函数,但这种方式无法利用ID索引,数据量大时性能极差,且若ID来源不可信存在SQL注入风险:

func DelAction(db *sql.DB, schema string, list []int) int64 {
    if len(list) == 0 {
        return 0
    }
    strList := []string{}
    for _, v := range list {
        strList = append(strList, strconv.Itoa(v))
    }
    param := strings.Join(strList, ",")
    // 修改SQL使用FIND_IN_SET
    delQuery := "DELETE FROM `schema1` WHERE FIND_IN_SET(`id`, ?)"
    delRes, err := db.Exec(delQuery, param)
    if err != nil {
        log.Fatal(err)
    }
    delCnt, err := delRes.RowsAffected()
    if err != nil {
        log.Fatal(err)
    }
    return delCnt
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 05:22:59