GoLang调用存储过程时QueryRowContext报错:参数数量不匹配
问题原因及解决方案
核心问题
你遇到的sql: expected 0 arguments, got 2错误,本质是数据库驱动不识别你用的@Email命名参数格式,导致Prepare解析SQL时认为没有需要绑定的参数,但你后续又传入了参数,两者产生冲突。另外,sql.Named的使用需要驱动明确支持,并非所有数据库驱动都兼容这种命名参数写法,且不同数据库的存储过程参数占位符规则存在差异。
针对不同数据库的解决方法
情况1:使用MySQL驱动(github.com/go-sql-driver/mysql)
MySQL驱动不支持@xxx格式的命名参数,也不兼容sql.Named,需改用?作为参数占位符:
func VerifyUser(user User) (*User, string, error) { db, ctx := db.GetDB() // 改用?作为参数占位符 query := "CALL usp_GetUserByUsername(?)" stmt, err := db.Prepare(query) if err != nil { log.Errorln("Error in preparing statement. " + err.Error()) return nil, "Error in preparing statement.", err } defer stmt.Close() // 直接传递参数值,无需sql.Named row := stmt.QueryRowContext(ctx, user.Email) var retUser User err = row.Scan(&retUser.ID, &retUser.Email, &retUser.Password, &retUser.Status) if err != nil { log.Warningln("Unknown Email: " + user.Email + ". " + err.Error()) return nil, "Invalid user.", err } return &retUser, "", nil }
情况2:使用SQL Server驱动(github.com/microsoft/go-mssqldb)
SQL Server驱动支持命名参数,可调整写法直接调用:
func VerifyUser(user User) (*User, string, error) { db, ctx := db.GetDB() // 保留存储过程的命名参数写法 query := "CALL usp_GetUserByUsername(@Email)" // 直接用QueryRowContext调用,无需提前Prepare(驱动会自动处理参数绑定) row := db.QueryRowContext(ctx, query, sql.Named("Email", user.Email)) var retUser User err := row.Scan(&retUser.ID, &retUser.Email, &retUser.Password, &retUser.Status) if err != nil { log.Warningln("Unknown Email: " + user.Email + ". " + err.Error()) return nil, "Invalid user.", err } return &retUser, "", nil }
额外注意点
- 部分数据库驱动对存储过程的
Prepare支持有限,直接使用QueryRowContext调用存储过程往往更可靠。 - 确保你的数据库驱动版本为最新,避免旧版本存在的参数解析bug。
内容的提问来源于stack exchange,提问作者Harsha W
相关产品推荐
相关产品推荐

