使用切片 IN 子句查询 Golang 数据库

5
有人可以解释一下为什么这个不起作用吗?
inq := "6,7" //strings.Join(artIds, ",")
rows, err = db.Query("SELECT DISTINCT title FROM tags_for_articles LEFT JOIN tags ON tags.id = tags_for_articles.tag_id WHERE article_id IN (?)", inq)

并且这样做

rows, err = db.Query("SELECT DISTINCT title FROM tags_for_articles LEFT JOIN tags ON tags.id = tags_for_articles.tag_id WHERE article_id IN (6,7)", inq)

我正在尝试使用一组整数完成一个简单的IN子句,但是每个建议的解决方案似乎都不是很惯用。 尝试了这样做,但问题似乎出现在字符串替换上。
inq := strings.Join(artIds, ",")

我有点惊讶,Go语言似乎没有优雅的方式处理这个查询。


1
为什么呢?因为inq是一个字符串,所以如果你将其参数化,最终会得到IN ('6,7')这样的结果,虽然它是有效的,但却是完全不同的东西。一般来说,IN是无法被参数化的。你看过这个吗:https://dev59.com/qmIj5IYBdhLWcg3wb0nX - Alex K.
谢谢,我就猜到会是这样。是的,我看到了接口重复的问题,但它让我感觉有点奇怪。 - Michael Mallett
这个回答解决了你的问题吗?如何在使用Golang的SQL中执行IN查询? - undefined
4个回答

4
由于 database/sql 不检查您的查询并直接将参数传递给驱动程序,因此处理带有 IN 子句的查询变得困难:
SELECT * FROM users WHERE level IN (?);

当这个语句在后端准备时,绑定变量“?”只对应单个参数,但通常希望根据某些切片的长度将其作为可变数量的参数。
var levels = []int{4, 6, 7}
rows, err := db.Query("SELECT * FROM users WHERE level IN (?);", levels)

有一种使用 sqlx 处理这些类型的查询的方法,该包提供了更多对数据库查询的控制。

通过使用 sqlx.In 首先处理查询可以实现这种模式:

var levels = []int{4, 6, 7}
query, args, err := sqlx.In("SELECT * FROM users WHERE level IN (?);", levels)

了解更多信息,请查看InQueries的Godoc。


谢谢提供的信息,非常有价值。那个软件包看起来不错。 - Michael Mallett

3

如果你已经小心地使用实际的整数构建了inq字符串(以避免注入),那么你可以自己构建字符串,避免使用“?”。

inq := "6,7" 
sql := fmt.Sprintf("SELECT DISTINCT title FROM tags_for_articles LEFT JOIN tags ON tags.id = tags_for_articles.tag_id WHERE article_id IN (%s)",inq)
rows, err := db.Query(sql)

如果您需要经常这样做,最好使用一个WhereIn函数来帮助您完成,或者使用一个ORM。但是请注意,您接受的参数应该非常谨慎,因为如果您接受任意字符串,可能会被注入任何内容。


我对Go语言还比较陌生,所以在动态类型语言中我会有些谨慎。然而,在我的代码中,inq是从一个int类型的切片生成的,因此它们肯定是整数。但这是一个好的实践吗? - Michael Mallett
如果你确保它们是整数,我认为这没有问题。你编写的任何函数都应该接受整数而不是字符串。 - Kenny Grant
好的,谢谢,这似乎是最简单的解决方案。实际上这是一个技术测试,所以我可能会对此进行详细说明,并展示它已经被考虑过了。 - Michael Mallett

3

您需要在 "IN" 子句中的问号数量与参数数量相匹配,因此您需要执行以下操作:

inq := "6,7" //strings.Join(artIds, ",")
qms := strings.Repeat("?,", len(inq))
qms = params[:len(params)-1] // remove the trailing ","

rows, err = db.Query("SELECT DISTINCT title FROM tags_for_articles LEFT JOIN tags ON tags.id = tags_for_articles.tag_id WHERE article_id IN (" + qms + ")", inq)

谢谢,这就是我在寻找的答案。 - Benjamin Collins

0

我曾经遇到过同样的问题,然后我想出了另一种更安全的解决方案。所以,你不需要担心 SQL 注入问题。

params := []string{"A", "B"}
jsonParams, err := json.Marshal(params)
if err != nil {
    return err
}

rows, err = db.Query(`
 WITH compared_values as(
   SELECT * 
     FROM JSON_TABLE(
      ?,
      "$[*]" COLUMNS( 
       value TEXT PATH "$"
     )
   ) as compared_values
 ) 
 SELECT * FROM target_table
 WHERE target_value IN (SELECT value FROM compared_values);
`, jsonParams)

你也可以像这样在 SQL 查询中使用纯选择部分,但我不确定性能如何。我认为使用 WITH 子句可以防止重复的 JSON 解析。

 SELECT * FROM  target_table
 WHERE target_value IN (
   SELECT value FROM JSON_TABLE(
     ?,
     "$[*]" COLUMNS( 
       value TEXT PATH "$"
     )
   ) as compared_values
  );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接