是否可以在 sql Slick 中对整数使用 IN 子句?

Is it possible to use IN clause in plain sql Slick for integers?

这里有一个类似的问题,但实际上并没有回答这个问题。

Is it possible to use IN clause in plain sql Slick?

请注意,这实际上是更大更复杂查询的一部分,因此我确实需要使用普通 sql 而不是 slick 的提升嵌入。像下面这样的东西会很好:

val ids = List(2,4,9)
sql"SELECT * FROM coffee WHERE id IN ($ids)"

sql前缀解锁一个StringContext,您可以在其中设置SQL参数。列表没有 SQL 参数,因此如果您不小心,很容易在此处打开 SQL 注入。关于使用 this question 上的 SQLServer 处理这个问题,有一些好的(和一些危险的)建议。您有几个选择:

你最好的选择可能是使用 #$ 运算符和 mkString 来插入动态 SQL:

val sql = sql"""SELECT * FROM coffee WHERE id IN (#${ids.mkString(",")})"""

这没有正确使用参数,因此可能会出现 SQL 注入和其他问题。

另一种选择是使用常规字符串插值和 mkString 构建语句:

val query = s"""SELECT * FROM coffee WHERE id IN (${ids.mkString(",")})"""
StaticQuery.queryNA[Coffee](query)

这与使用 #$ 的方法基本相同,但在一般情况下可能更灵活。

如果 SQL 注入漏洞是一个主要问题(例如,如果 ids 的元素是用户提供的),您可以为 [=19= 的每个元素构建一个带有参数的查询].然后你需要提供一个自定义 SetParameter 实例,以便 slick 可以将 List 转换为参数:

implicit val setStringListParameter = new SetParameter[List[String]]{
    def apply(v1: List[String], v2: PositionedParameters): Unit = {
        v1.foreach(v2.setString)
    }
}

val idsInClause = List.fill(ids.length)("?").mkString("(", ",", ")")
val query = s"""SELECT * FROM coffee WHERE id IN ($idsInClause)"""
Q.query[List[String], String](query).apply(ids).list(s)

因为你的 idsInts,这可能不是一个问题,但如果你更喜欢这种方法,你只需要将 setStringListParameter 更改为使用 Int 而不是 String:

  val ids = List(610113193610210035L, 220702198208189710L)

  implicit object SetListLong extends SetParameter[List[Long]] {
    def apply(vList: List[Long], pp: PositionedParameters) {
      vList.foreach(pp.setLong)
    }
  }

  val select = sql"""
        select idnum from idnum_0
        where idnum in ($ids#${",?" * (ids.size - 1)})
    """.as[Long]

@Ben Reich 是对的。 这是另一个示例代码,在 slick 3.1.0 上测试。

($ids#${",?" * (ids.size - 1)})

虽然这不是普遍的答案,也可能不是作者想要的,但我仍然想向看到这个问题的人指出这一点。

一些数据库后端支持数组类型,并且有 Slick 的扩展允许在插值中设置这些数组类型。

例如,Postgres 的语法为 where column = any(array),对于 slick-pg,您可以像这样使用此语法:

def query(ids: Seq[Long]) = db.run(sql"select * from table where ids = any($ids)".as[Long])

这带来了更简洁的语法,这对语句编译器缓存更友好,并且还可以避免 SQL 注入和使用 #$var 插值创建格式错误的 SQL 的总体危险语法。

当尝试在 MySQL 的 IN 查询中使用 Seq[Long] 时,

运行 在 Slick 3.3.3 中遇到了本质上相同的问题。不断收到 Slick of 的编译错误:

could not find implicit value for parameter e: slick.jdbc.SetParameter[Seq[Long]]

最初的问题应该是这样的:

could not find implicit value for parameter e: slick.jdbc.SetParameter[List[Int]]

Slick 3.3.X+ 可以处理 IN 查询的参数绑定,只要我们提供 Slick 应该如何为我们使用的类型执行此操作的隐式定义。这意味着在 class 级别的某处添加 implicit val 定义。所以,比如:

class MyClass {
  // THIS IS THIS KEY LINE TO ENABLE SLICK TO BIND THE PARAMS
  implicit val setListInt = SetParameter[List[Int]]((inputList, params) => inputList.foreach(params.setInt))

  def queryByHardcodedIds() = {
    val ids: List[Int] = List(2,4,9)

    sql"SELECT * FROM coffee WHERE id IN ($ids)" // SLICK CAN AUTO-HANDLE BINDING NOW
  }
}

与 Seq[Long] 及其他情况类似。只需确保您的 types/binding 符合您需要 Slick 处理的内容:

implicit val setSeqLong = SetParameter[Seq[Long]]((inputList, params) => inputList.foreach(params.setLong))
// ^^Note the `SetParameter[Seq[Long]]` & `.setLong` for type alignment

我已经为 Slick 写了一个小的扩展来解决这个问题:https://github.com/rtkaczyk/inslick

对于给定的示例,解决方案是:

import accode.inslick.syntax._

val ids = List(2,4,9)
sqli"SELECT * FROM coffee WHERE id IN *$ids"

此外,InSlick 还可以处理元组或大小写的迭代器 类。它适用于所有 Slick 3.x 版本和 Scala 版本 2.11 - 2.13。在我工作的公司,我们已经在生产中使用它几个月了。

插值可以避免 SQL 注入。它利用一个宏以类似于

的方式重写查询