[Go] 关于 SQL 中 IN 子句参数的传递

陪她去流浪 桃子 2018年10月07日 编辑 阅读次数:3976

在Go中使用GORM操作数据库的时候,它的IN子句是支持直接使用切片作为参数的。比如:

1
2
names := []string{"one", "two", "three"}
db.Where("name IN (?)", names)

这样使用时,IN子句的?被绑定到整个切片上,GORM内部会自动根据参数的个数把问号转换成对应的个数,这会带来极大的便利。比如上面的name IN (?)会在内部被替换成name IN (?,?,?)

但是,GO语言原生的SQL不支持这么做。它的每一个?只绑定一个参数。所以在使用原生SQL的IN子句的时候,需要动态计算问号的个数

原生的Query查询语句是这样的:

1
func Query(query string, args ...interface{}) (*sql.Rows, error)

query里面有多少个?args的个数就得多少个。

假设我们要这样一个查询(IN的参数个数不确定):

1
SELECT * FROM users_tab WHERE name IN ('one','two','three')

首先,我们需要知道参数的个数,求出问号的个数:

1
2
3
4
5
6
7
func CreateQuestionMarks(n int) string {
    s := "?"
    for i := 1; i < n; i++ {
        s += ",?"
    }
    return s
}

第二,Queryargs[]interface{},但由于Go不支持把非interface切片自动转换成interface切片,所以这一步也得手动来做:

1
2
3
4
5
6
7
func ConvertToInterfaces(slice []string) []interface{} {
	s := make([]interface{}, len(slice))
	for i, v := range slice {
		s[i] = v
	}
	return s
}

最后,完成Query的调用:

1
2
3
query := fmt.Sprintf("SELECT * FROM users_tab WHERE name IN (%s)", CreateQuestionMarks(len(names)))
args := ConvertToInterfaces(names)
rows, err := db.Query(query, args...)

总之,这个过程很艰辛。

标签:Go · 数据库